线程内的线程试图调用和更新ui

时间:2013-10-05 15:12:26

标签: c# multithreading winforms asynchronous async-await

我在winform上使用文本框,在textchanged上执行后台线程:

private void txtFathersLast_TextChanged(object sender, EventArgs e)
{
ThreadPool.QueueUserWorkItem(_ => WaitWhileUserTyping());
}
private void WaitWhileUserTyping()
        {
            var keepWaiting = true;

            while (keepWaiting)
            {
                _keyPressed = false;

                Thread.Sleep(TypingDelay);

                keepWaiting = _keyPressed;
            }

            Invoke((MethodInvoker)(ExecuteSearch));

            _waiting = false;
        }


private void ExecuteSearch()
        {
            Thread.Sleep(200);

            Task.Factory.StartNew(() =>
            {

                using (DataReference.SearchWCF search = new DataReference.SearchWCF())
                {
                    _similaritySearchResults = search.SearchPersonBySimilarity(txtFathersLast.Text, txtMothersLast.Text, txtName.Text, DateTime.Now, 10);
                }

            }).ContinueWith(t=>{

                if (this.InvokeRequired)
                {
                    this.BeginInvoke(new Action(() =>
                    {
                        if (_similaritySearchResults != null && _similaritySearchResults.Tables["data"].Rows.Count > 0)
                        {
                            DataTable dt = _similaritySearchResults.Tables["data"];

                            Infragistics.Win.Misc.UltraTile newTile = null;

                            for (int index = 0; index < dt.Rows.Count; index++)
                            {
                                newTile = new Infragistics.Win.Misc.UltraTile("Person X");
                                newTile.Control = new CustomControls.Controls.PersonResult("123", "123", index + 150);
                                newTile.Tag = new Guid("90D27721-7315-4B86-9CFD-4F7D02921E9A");
                                newTile.DoubleClick += TileDoubleClick;
                                tilePanel.Tiles.Add(newTile);
                            }
                        }

                    }));
                }
                else
                {
                    if (_similaritySearchResults != null && _similaritySearchResults.Tables["data"].Rows.Count > 0)
                    {
                        DataTable dt = _similaritySearchResults.Tables["data"];

                        Infragistics.Win.Misc.UltraTile newTile = null;

                        for (int index = 0; index < dt.Rows.Count; index++)
                        {
                            newTile = new Infragistics.Win.Misc.UltraTile("Person X");
                            newTile.Control = new CustomControls.Controls.PersonResult("123", "123", index + 150);
                            newTile.Tag = new Guid("90D27721-7315-4B86-9CFD-4F7D02921E9A");
                            newTile.DoubleClick += TileDoubleClick;
                            tilePanel.Tiles.Add(newTile);
                        }
                    }
                }




            }, TaskScheduler.FromCurrentSynchronizationContext());


        }

这很好用,应用程序转到数据库然后获取结果并更新UI,根据数据库返回的记录数量向控件添加切片。

现在,当我尝试将另一个后台线程添加到我的自定义控件中时,问题出现了:

new CustomControls.Controls.PersonResult(“123”,“123”,index + 150);

控件的代码是:

protected override void InitLayout()
        {
            // if I comment this then everything works fine
            // but if I leave this, then the UI freezes!!
            GetPictureAsync();

            base.InitLayout();
        }

/// <summary>
        /// 
        /// </summary>
        private void GetPictureAsync()
        {
            // This line needs to happen on the UI thread...
            TaskScheduler uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();


            Task.Factory.StartNew(() =>
            {
                Random sleep = new Random();
                System.Threading.Thread.Sleep(sleep.Next(1000,3000));
                if (this.pbPhoto.InvokeRequired)
                {
                    this.pbPhoto.Invoke(new Action(() =>
                    {
                        this.Load(@"E:\Photos\" + PhotoId.ToString() + ".jpg");
                        //this.pbPhoto.Image = Utility.Common.GetResourceImage("woman_sample.jpg");
                    }));
                }
                else
                {
                    this.Load(@"E:\Photos\" + PhotoId.ToString() + ".jpg");
                    //this.pbPhoto.Image = Utility.Common.GetResourceImage("woman_sample.jpg");
                }


            }, CancellationToken.None, TaskCreationOptions.None, uiScheduler);
        }

所以问题似乎是我首先执行一个线程来查看何时开始搜索,然后在该线程内我运行另一个线程以便从数据库中获取数据,然后在UI中更新的每个控件将运行另一个线程获取图片并更新图片框。

任何人都知道如何解决这个问题?或解决这个问题的方法?

2 个答案:

答案 0 :(得分:0)

致电时

new CustomControls.Controls.PersonResult("123", "123", index + 150)

“123”是文字字符串,还是从UI控件中读取。例如,

new CustomControls.Controls.PersonResult(txtFathersName.Text", txtMothersName.Text, index + 150)

我现在无法测试它,但是不能从创建控件的线程以外的线程访问Text属性吗?

答案 1 :(得分:0)

我认为问题在于你强迫GetPictureAsync中的任务在UI线程上执行,然后你正在调用Thread.Sleep()。这个update UI in Task using TaskScheduler.FromCurrentSynchronizationContext问题可以解决您遇到的同样问题。我会将您的代码重写为:

private void async GetPictureAsync()
{
    Random sleep = new Random();
    await TaskEx.Delay(sleep.Next(1000,3000));
    this.Load(@"E:\Photos\" + PhotoId.ToString() + ".jpg");
}