none
如何在Async與Await執行時扣住執行緒不放? RRS feed

  • 問題

  • 因為有好幾個預定要實作的功能會共用產生RichTextBlock的內容,

    所以試著把產生RichTextBlock的程式碼給移出變成共用的函數

    this.Content = await RichTextTemplate.GetRichTextBlockAsync(xmlRoot, App.dataSource.DataVersionChanged);

    但當我執行的時候,卻發生了System.NullReferenceException的錯誤

    雖然檢查之下發現是用來確認資料有沒有需要更新的變數(放在App的DataSource中,因為使用到網路連接所以已經用了Await)沒有初始化

    只是我的RichTextBlock建置程式碼中必須要透過這個變數來確定需不需要重新下載圖片所以非利用這個變數不可

    但是我不知道該如何扣住這邊建置RichTextBlock的執行緒不放,等到獲得更新與否的函式執行續跑完了才繼續下去......

    現在雖然是用一個DispatcherTimer設定成沒抓到而跑出例外的話,就放著不管直到抓到值了才繼續下去

    DispatcherTimer timer = new DispatcherTimer();
    timer.Interval = TimeSpan.FromSeconds(1);
    timer.Tick += async (s, args) =>
        {
            bool boolDataChecked = false;
            try
            {
                var a = App.dataSource.DataVersionChanged;
                boolDataChecked = true;
            }
            catch
            {
                boolDataChecked = false;
            }
    
            if (boolDataChecked)
            {
                timer.Stop();
                timer = null;
                this.Content = await RichTextTemplate.GetRichTextBlockAsync(xmlRoot, App.dataSource.DataVersionChanged);
                this.Content.Width = 400;
                this.Content.IsTextSelectionEnabled = false;
    
                this.Loaded = true;
            }
        };

    可是某個程度上好像太沒效率了......(默


    • 已編輯 WildDagger 2013年11月17日 上午 08:43
    2013年11月17日 上午 08:40

解答

  • public async void LoadDetailData()

    改成

    public async Task LoadDetailData()

    然後呼叫時加上 await LoadDetailData() 這樣就會等了.


    在現實生活中,你和誰在一起的確很重要,甚至能改變你的成長軌跡,決定你的人生成敗。 和什麼樣的人在一起,就會有什麼樣的人生。 和勤奮的人在一起,你不會懶惰; 和積極的人在一起,你不會消沈; 與智者同行,你會不同凡響; 與高人為伍,你能登上巔峰。

    2013年11月18日 下午 01:43
    版主

所有回覆

  • 你原來這一行 this.Content = await RichTextTemplate.GetRichTextBlockAsync(xmlRoot, App.dataSource.Data

    所在的函式宣告是如何宣告, 而你又是如何呼叫他的 ?


    在現實生活中,你和誰在一起的確很重要,甚至能改變你的成長軌跡,決定你的人生成敗。 和什麼樣的人在一起,就會有什麼樣的人生。 和勤奮的人在一起,你不會懶惰; 和積極的人在一起,你不會消沈; 與智者同行,你會不同凡響; 與高人為伍,你能登上巔峰。

    2013年11月17日 上午 08:50
    版主
  • using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using Windows.UI.Core;
    using Windows.UI.Xaml;
    using Windows.UI.Xaml.Controls;
    using Windows.UI.Xaml.Documents;
    using Windows.UI.Text;
    using System.Xml.Linq;

    namespace WDFunc { public class RichTextTemplate { public static async Task<RichTextBlock> GetRichTextBlockAsync(XElement xmlDoc, bool changed) { RichTextBlock rtb = new RichTextBlock(); var Paragraphs = xmlDoc.Descendants("Paragraph"); foreach (var a in Paragraphs) { Paragraph para = new Paragraph(); para.Margin = new Windows.UI.Xaml.Thickness(10); var xmlSection = a.Descendants("Section"); foreach (var b in xmlSection) { string content = (string)b; string type = (string)b.Attribute("Type"); switch (type) { case "Header": Run runHeader = new Run(); runHeader.Text = content; runHeader.FontSize = 28; runHeader.FontWeight = FontWeights.Bold; para.Inlines.Add(runHeader); para.Inlines.Add(new LineBreak()); break; case "Break": break; case "Image": InlineUIContainer iui = new InlineUIContainer(); Image img = new Image(); img.Source = await FileStorage.LoadBitmapImageAsync(content, changed); img.Width = 380; img.Stretch = Windows.UI.Xaml.Media.Stretch.Uniform; img.Margin = new Windows.UI.Xaml.Thickness(5); iui.Child = img; para.Inlines.Add(iui); break; case "Text": Run runText = new Run(); runText.Text = content; runText.FontSize = 20; para.Inlines.Add(runText); break; case "Hyperlink": Hyperlink hyperlink = new Hyperlink(); string attUrl = (string)b.Attribute("Url"); if (attUrl != string.Empty) { hyperlink.NavigateUri = new Uri(attUrl); } else { hyperlink.NavigateUri = new Uri(content); } para.Inlines.Add(hyperlink); break; case "Mix": var c = b.Descendants("Section"); foreach (var d in c) { string contentInner = (string)d; string typeInner = (string)d.Attribute("Type"); switch (typeInner) { case "Text": Run runTextInner = new Run(); runTextInner.Text = contentInner; runTextInner.FontSize = 20; para.Inlines.Add(runTextInner); break; case "Image": InlineUIContainer IUIInner = new InlineUIContainer(); Image imgInner = new Image(); imgInner.Source = await FileStorage.LoadBitmapImageAsync(contentInner, changed); IUIInner.Child = imgInner; para.Inlines.Add(IUIInner); break; case "Hyperlink": Hyperlink hyperlinkInner = new Hyperlink(); string attUrlInner = (string)d.Attribute("Url"); if (attUrlInner != string.Empty) { hyperlinkInner.NavigateUri = new Uri(attUrlInner); } else { hyperlinkInner.NavigateUri = new Uri(contentInner); } para.Inlines.Add(hyperlinkInner); break; case "Break": para.Inlines.Add(new LineBreak()); break; } } break; } para.Inlines.Add(new LineBreak()); } rtb.Blocks.Add(para); } return rtb; } } }

    大致上是這樣,裡面用到的另一個函數是

    public static async Task<BitmapImage> LoadBitmapImageAsync(string url, bool changed)
    {
        CancellationTokenSource _tokenSource = new CancellationTokenSource(5000);
        CancellationToken token = _tokenSource.Token;
        BitmapImage image = new BitmapImage();
        //分析結構
        string urlBase = url.Substring(0, 7);
        if (urlBase == "http://")
        {
            string urlFull = url.Substring(7);
            string[] urlArray = urlFull.Split('/');
            string fileName = string.Empty;
            for (int i = 0; i < urlArray.Count(); i++)
            {
                if (i == 0)
                {
                    fileName += urlArray[i];
                }
                else
                {
                    fileName += ("." + urlArray[i]);
                }
            }
            fileName += ".wdTemp";
            var folder = await ApplicationData.Current.LocalFolder.CreateFolderAsync("ImgTemp", CreationCollisionOption.OpenIfExists).AsTask(token);
            bool fileAccesSuccess = false;
            try
            {
                var file = await folder.GetFileAsync(fileName);
                image = await LoadOfflineImage(file);
                fileAccesSuccess = true;
            }
            catch
            {
                fileAccesSuccess = false;
            }
            if (!fileAccesSuccess || changed)
            {
                //讀取失敗或需要更新
                var file = await folder.CreateFileAsync(fileName, CreationCollisionOption.ReplaceExisting).AsTask(token);
                image = await SaveOfflineImage(file, url);
            }
        }
        return image;
    }
    存儲檔案的部分我覺得不是出問題的地方就請恕我不列了
    2013年11月17日 下午 05:13
  • 我要問的是外面的東西..比方

    public void methodname()

    {

        .....

       this.content = .....

    }


    在現實生活中,你和誰在一起的確很重要,甚至能改變你的成長軌跡,決定你的人生成敗。 和什麼樣的人在一起,就會有什麼樣的人生。 和勤奮的人在一起,你不會懶惰; 和積極的人在一起,你不會消沈; 與智者同行,你會不同凡響; 與高人為伍,你能登上巔峰。

    2013年11月18日 上午 06:31
    版主
  • async 與 await 基本上就是該 thread 會停留在 await 這個位置.


    在現實生活中,你和誰在一起的確很重要,甚至能改變你的成長軌跡,決定你的人生成敗。 和什麼樣的人在一起,就會有什麼樣的人生。 和勤奮的人在一起,你不會懶惰; 和積極的人在一起,你不會消沈; 與智者同行,你會不同凡響; 與高人為伍,你能登上巔峰。

    2013年11月18日 上午 06:33
    版主
  •     public class MetroArticle : MetroDataBase, ILoadDetailData
        {
            private string _service { get; set; }
            public string Service { get { return _service; } set { _service = value; } }
            private RichTextBlock _content = new RichTextBlock();
            public RichTextBlock Content { get { return _content; } set { _content = value; } }
    
            public async void LoadDetailData()
            {
                string appID = this.AppID;
                XDocument xmlDetail = XDocument.Load(BaseCommands.FilePathDetailData(DataType.Information, this._service, appID));
    
                var xmlRoot = (from a in xmlDetail.Descendants("Content") select a).First();
    
                
                this.Content = await RichTextTemplate.GetRichTextBlockAsync(xmlRoot, App.dataSource.DataVersionChanged);
                this.Content.Width = 400;
                this.Content.IsTextSelectionEnabled = false;

                this.Loaded = true; } } interface ILoadDetailData { void LoadDetailData(); }
    如果沒有用DispatchTimer的話,原本的程式碼長這個樣子

    • 已編輯 WildDagger 2013年11月18日 上午 11:18
    2013年11月18日 上午 11:16
  • public async void LoadDetailData()

    改成

    public async Task LoadDetailData()

    然後呼叫時加上 await LoadDetailData() 這樣就會等了.


    在現實生活中,你和誰在一起的確很重要,甚至能改變你的成長軌跡,決定你的人生成敗。 和什麼樣的人在一起,就會有什麼樣的人生。 和勤奮的人在一起,你不會懶惰; 和積極的人在一起,你不會消沈; 與智者同行,你會不同凡響; 與高人為伍,你能登上巔峰。

    2013年11月18日 下午 01:43
    版主
  • 加上去後,有些情況會成功,但是有的時候還是不會等(比方說下載網路圖檔的時候)

    比方說這個,我本來希望可以卡在紅線處等待所有陣列中的元素都跑完這個需要等待的函式

    但是當我實際執行的時候,根本沒有等裡面下載圖片的執行緒跑完,程式就自顧自跑下去

    最後就變成這樣:

    但是當我跳出去再重新點進來的時候,圖就出來了

    檢查的時候發現因為主執行緒根本沒有等載入圖片的執行緒跑完就去抓圖片

    所以得到的圖片變數是null......

    2013年11月21日 下午 04:36
  •  資料類別加上 INotifyPropertyChanged 以便在資料改變時通知 UI 改變


    在現實生活中,你和誰在一起的確很重要,甚至能改變你的成長軌跡,決定你的人生成敗。 和什麼樣的人在一起,就會有什麼樣的人生。 和勤奮的人在一起,你不會懶惰; 和積極的人在一起,你不會消沈; 與智者同行,你會不同凡響; 與高人為伍,你能登上巔峰。

    2013年11月22日 上午 04:52
    版主
  • 雖然成功了,可是測試時發現好像是因為IO關係,記憶體會飆到700MB左右......
    2013年11月28日 上午 11:00
  • 雖然成功了,可是測試時發現好像是因為IO關係,記憶體會飆到700MB左右......

    你拉下來的資料量很大嗎 ?

    既然成功了, 那原來的問題是怎麼解決的 ?


    在現實生活中,你和誰在一起的確很重要,甚至能改變你的成長軌跡,決定你的人生成敗。 和什麼樣的人在一起,就會有什麼樣的人生。 和勤奮的人在一起,你不會懶惰; 和積極的人在一起,你不會消沈; 與智者同行,你會不同凡響; 與高人為伍,你能登上巔峰。


    2013年11月28日 下午 03:29
    版主
  • 只有30MB左右的圖

    但是第一次讀取的時候記憶體就會飆到200MB

    然後存好離線檔後,第二次讀取使用離線檔的時候使用記憶體就莫名其妙飆到700MB

    -------------------------

    原本的問題就是把所有的await void通通改成await Task

    雖然有碰上Dispatcher執行緒分配錯誤但是有找到Dispatcher.RunAsync的方法

    現在雖然有想過用CancellationToken但是完全沒有效用

    在網路上搜尋找到的也多半是答非所問的東西......

    2013年11月28日 下午 03:37
  • 檢查看看有哪個環節的變數(尤其是影像, 檔案, 串流相關類別的變數) 存留時期太久, 或於適當的位置呼叫 GC.Collect() 強制回收記憶體.


    在現實生活中,你和誰在一起的確很重要,甚至能改變你的成長軌跡,決定你的人生成敗。 和什麼樣的人在一起,就會有什麼樣的人生。 和勤奮的人在一起,你不會懶惰; 和積極的人在一起,你不會消沈; 與智者同行,你會不同凡響; 與高人為伍,你能登上巔峰。

    2013年11月28日 下午 03:55
    版主
  • 第一次啟動的時候確實有從200MB掉回100MB左右,但是第二次啟動讀取離線檔的時候仍然是700MB左右沒變

    public static async Task<BitmapImage> LoadOfflineImage(StorageFile file)
            {
                BitmapImage bitmap = new BitmapImage();
                try
                {
                    //CancellationTokenSource _tokenSource = new CancellationTokenSource(5000);
                    //CancellationToken token = _tokenSource.Token;
                    string strBase = await FileIO.ReadTextAsync(file);
                    byte[] bytes = Convert.FromBase64String(strBase);
    
                    using (MemoryStream ms = new MemoryStream(bytes))
                    {
                        await bitmap.SetSourceAsync(ms.AsRandomAccessStream());
                    }
    
                }
                catch(Exception)
                {
                    //return null;
                }
                return bitmap;
            }

    我猜是這段程式碼有問題(因為這段就是讀取離線圖片檔)

    可是我不知道怎麼辦才好,已經試過在using裡面指定好bitmap的串流後呼叫ms.Dispose()、bytes = null或者是GC.Collect()全部都沒用,甚至還不減反增......

    2013年11月28日 下午 04:20
  • 老實說,  100MB 的記憶體我很懷疑過的了審核沒有.

    在現實生活中,你和誰在一起的確很重要,甚至能改變你的成長軌跡,決定你的人生成敗。 和什麼樣的人在一起,就會有什麼樣的人生。 和勤奮的人在一起,你不會懶惰; 和積極的人在一起,你不會消沈; 與智者同行,你會不同凡響; 與高人為伍,你能登上巔峰。

    2013年11月28日 下午 05:43
    版主
  • 其實有過的說......

    所以我一直沒發現Memory Leak的問題Orz

    2013年11月29日 上午 02:45