C# 后台线程如何告诉 UI 线程它已经完成了某项任务?

18

方案

假设你有一个C# WinForms应用程序,正在进行一些数据处理。你有一个从数据库中检索数据的方法,该方法由UI线程调用。后台线程然后运行此任务。你希望UI继续执行其任务而不被锁定和无响应。

问题

如何让后台线程运行并执行其处理,然后在其返回结果时自动通知UI线程?

10个回答

13
如果您不使用后台工作线程(出于任何原因),那么您必须从您的线程触发一个事件,该事件由UI线程处理。例如,我有这段代码来扫描我的mp3,并为每个找到的专辑触发一个事件,然后在完成(或停止)时再触发另一个事件:
    public void Build()
    {
        FindAlbums(Root);

        // Final update
        if (Library_Finished != null)
        {
            Library_Finished(this, null);
        }
    }

    private void FindAlbums(string root)
    {
        // Find all the albums
        string[] folders = Directory.GetDirectories(root);
        foreach (string folder in folders)
        {
            string[] files = Directory.GetFiles(folder, "*.mp3");
            if (files.Length > 0)
            {
                // Add to library - use first file as being representative of the whole album
                var info = new AlbumInfo(files[0]);
                if (Library_AlbumAdded != null)
                {
                    Library_AlbumAdded(this, new AlbumInfoEventArgs(info));
                }
            }

            FindAlbums(folder);
        }
    }

然后在UI线程中(这是WinForms代码):

    private void Library_AlbumAdded(object sender, AlbumInfoEventArgs e)
    {
        if (dataGridView.InvokeRequired)
        {
            dataGridView.Invoke((MethodInvoker)delegate { AddToGrid(e.AlbumInfo); });
        }
        else
        {
            AddToGrid(e.AlbumInfo);
        }
    }

    private void Library_Finished(object sender, EventArgs e)
    {
        if (dataGridView.InvokeRequired)
        {
            dataGridView.Invoke((MethodInvoker)delegate { FinalUpdate(); });
        }
        else
        {
            FinalUpdate();
        }
    }

然而,我建议您调查后台工作线程,因为它可以为您处理很多后勤工作。但是,在RunWorkerCompleted事件中需要相同的处理代码来更新用户界面。


6

3
你可以使用BackgroundWorker在其DoWork事件处理程序中执行耗时的处理。然后处理RunWorkerComplete事件--当DoWork方法完成时它会触发。在所有这些操作进行时,你的UI线程将愉快地运行。

2
下次我会打得更快的。 :) - Adam Lear

0

正如多次提到的那样,可以使用BackgroundWorker类。

或者,您可以执行类似以下操作的操作:

void buttonGo_Clicked( object sender, EventArgs e )
{
    MyAsyncClass class = new MyAsyncClass();
    class.LongOperationFinished += (LongOperationFinishedEventHandler)finished;
    class.BeginLongOperation();
}

void finished( object sender, EventArgs e )
{
    if( this.InvokeRequired ) {
        this.BeginInvoke( (LongOperationFinishedEventHandler)finished, sender, e );
        return;
    }
    // You can safely modify the gui here.
}

0
如果您正在谈论WinForm应用程序,可以使用表单(或表单上任何控件)上的Invoke方法更改任何UI对象。您可能还会发现InvokeRequired属性非常有用。

0

如果您正在使用.NET 2.0或更高版本,则可以使用BackgroundWorker线程轻松实现此目的。它有自己的RunWorkerCompleted事件,正好可以满足您的需求。

事实上,我强烈推荐使用BackgroundWorker。它具有大多数开发人员在创建线程时需要的功能。它们也更容易优雅地取消,并且甚至具有报告进度的能力。


0
尝试使用BackgrounWorker并注册一个处理程序到其RunWorkerCompleted事件。

0
在Winforms中,您可以使用.Invoke方法(并检查.InvokeRequired属性)将回调调用传递到UI线程。您不需要通知UI线程 - 它会继续运行而不等待任何完成,但是您可以使用Invoke方法从另一个线程与控件交互(例如,更新标签的文本属性)。
您还可以使用BackgroundWorker对象(阅读MSDN以了解更多信息),它实现了回调功能,在后台工作完成后在UI线程上运行一些代码。

0

你可以使用Dispatcher.CurrentDispatcher在GUI线程调用的方法中存储对UI线程Dispatcher的引用。使用此对象,您可以在工作线程中使用BeginInvoke或Invoke方法来执行一个方法,通知GUI线程您已完成工作。个人认为,这种方法比使用后台工作器对象稍微灵活一些,并且可以生成稍微更易读的代码。


0

在C#中,有一种简单的处理多线程的方法,它被称为BackgroundWorker。你应该看看这个:BackgroundWorker教程


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接