C# 线程完成时与UI线程通信

C# 线程完成时与UI线程通信,c#,multithreading,C#,Multithreading,下面我有一个简单的程序,有两个线程执行一些任务 Thread1是数据馈送器。Thread2是数据处理器 到目前为止,通过我的方法完成的工作是有效的,但我希望在工作完成时有更好的方式得到通知 这是密码 class Program { private static BlockingCollection<int> _samples = new BlockingCollection<int>(); private static CancellationToken

下面我有一个简单的程序,有两个线程执行一些任务

Thread1是数据馈送器。Thread2是数据处理器

到目前为止,通过我的方法完成的工作是有效的,但我希望在工作完成时有更好的方式得到通知

这是密码

class Program 
{
    private static BlockingCollection<int> _samples = new BlockingCollection<int>();
    private static CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
    private static bool _cancel;


    static void Main(string[] args)
    {
        ThreadStart thread1 = delegate
        {
            ProcessThread1();
        };
        new Thread(thread1).Start();

        ThreadStart thread2 = delegate
        {
            ProcessThread2();
        };
        new Thread(thread2).Start();

        Console.WriteLine("Press any key to cancel..");

        Console.Read();

        _cancel = true;
        _cancellationTokenSource.Cancel();

        Console.Read();

    }

    private static void ProcessThread1()
    {
        for (int i = 0; i < 10; i++)
        {
            if (_cancel)
            {
                break;
            }

            Console.WriteLine("Adding data..");

            _samples.TryAdd(i,100);

            Thread.Sleep(1000);
        }

        // I dont like this. Instead can I get notified in the UI thread that this thread is complete.
        _cancel = true;
        _cancellationTokenSource.Cancel();

    }

    private static void ProcessThread2()
    {
        while (!_cancellationTokenSource.IsCancellationRequested)
        {
            int data;

            if (_samples.TryTake(out data, 100)) 
            {
                // Do some work.    
                Console.WriteLine("Processing data..");
            }

        }

        Console.WriteLine("Cancelled.");
    }


}
类程序
{
私有静态BlockingCollection_samples=新BlockingCollection();
私有静态CancellationTokenSource _CancellationTokenSource=new CancellationTokenSource();
私有静态bool\u cancel;
静态void Main(字符串[]参数)
{
ThreadStart thread1=委托
{
ProcessThread1();
};
新线程(thread1.Start();
ThreadStart thread2=委托
{
ProcessThread2();
};
新线程(thread2.Start();
Console.WriteLine(“按任意键取消…”);
Console.Read();
_取消=真;
_cancellationTokenSource.Cancel();
Console.Read();
}
私有静态void ProcessThread1()
{
对于(int i=0;i<10;i++)
{
如果(取消)
{
打破
}
Console.WriteLine(“添加数据…”);
_样本.TryAdd(i,100);
睡眠(1000);
}
//我不喜欢这样。相反,我可以在UI线程中得到通知,该线程已完成。
_取消=真;
_cancellationTokenSource.Cancel();
}
私有静态void ProcessThread2()
{
而(!\u cancellationTokenSource.IsCancellationRequested)
{
int数据;
如果(_samples.TryTake(输出数据,100))
{
//做一些工作。
Console.WriteLine(“处理数据…”);
}
}
控制台。写入线(“取消”);
}
}
如果用户请求取消或工作完成,我希望程序退出


我不确定当ProcessThread1用完时如何得到通知。目前,当工作完成时,我正在设置cancel=true,但它似乎不正确。非常感谢您的帮助。

如果您使用
任务而不是手动创建线程,您可以在任务上附加一个延续,以通知UI工作已完成

Task workOne = Task.Factory.StartNew( () => ProcessThread1());

workOne.ContinueWith(t =>
{
     // Update UI here
}, TaskScheduler.FromCurrentSynchronizationContext());
使用.NET 4.5,这变得更加容易,因为您可以潜在地使用新的
async
语言支持:

var workOne = Task.Run(ProcessThread1);
var workTwo = Task.Run(ProcessThread2);

// asynchronously wait for both tasks to complete...
await Task.WhenAll(workOne, workTwo);

// Update UI here.

请注意,这两者的设计都考虑了用户界面,并且在控制台应用程序中的行为会异常,因为控制台应用程序中没有当前的同步上下文。当您将其移动到真正的用户界面时,它将正常工作。

再启动一个线程,其唯一任务是等待控制台输入:

private void ConsoleInputProc()
{
    Console.Write("Press Enter to cancel:");
    Console.ReadLine();
    _cancellationTokenSource.Cancel();
}
然后,主线程启动两个处理线程和输入线程

// create and start the processing threads
Thread t1 = new Thread(thread1);
Thread t2 = new Thread(thread2);
t1.Start();
t2.Start();

// create and start the input thread
Thread inputThread = new Thread(ConsoleInputProc);
inputThread.Start();
然后,等待两个处理线程:

t1.Join();
// first thread finished. Request cancellation.
_cancellationTokenSource.Cancel();
t2.Join();
因此,如果用户按Enter键,那么输入线程将设置取消标志。thread1和thread2都会看到取消请求并退出

如果thread1完成其工作,则主线程设置取消标志,thread2将取消

在这两种情况下,直到线程2退出,程序才会退出

没有必要显式地终止输入线程。当程序退出时,它将死亡

顺便说一下,我将从Thread1进程中删除以下行:

    // I dont like this. Instead can I get notified in the UI thread that this thread is complete.
    _cancel = true;
    _cancellationTokenSource.Cancel();
我将删除
\u cancel
变量,并像第二个线程一样,让第一个线程检查
IsCancellationRequested

不幸的是,您必须启动一个专用线程来等待控制台输入,但这是我所知道的实现这一点的唯一方法。Windows控制台似乎没有可等待的事件

请注意,您可以使用
任务
执行相同的操作,总体上更易于使用。任务执行的代码是相同的

更新 从更大的角度来看,我发现您有一个典型的生产者/消费者设置,带有
BlockingCollection
。您可以使生产者和消费者线程更干净:

private static void ProcessThread1()
{
    for (int i = 0; i < 10; i++)
    {
        Console.WriteLine("Adding data..");
        _samples.TryAdd(i, Timeout.Infinite, _cancellationTokenSource.Token);
        // not sure why the sleep is here
        Thread.Sleep(1000);
    }
    // Marks the queue as complete for adding.
    // When the queue goes empty, the consumer will know that
    // no more data is forthcoming.
    _samples.CompleteAdding();
}

private static void ProcessThread2()
{
    int data;
    while (_samples.TryTake(out data, TimeSpan.Infinite, _cancellationTokenSource.Token))
    {
        // Do some work.    
        Console.WriteLine("Processing data..");
    }

    Console.WriteLine("Cancelled.");
}
private static void ProcessThread1()
{
对于(int i=0;i<10;i++)
{
Console.WriteLine(“添加数据…”);
_TryAdd(i,Timeout.Infinite,_cancellationTokenSource.Token);
//不知道为什么这里有睡眠
睡眠(1000);
}
//将队列标记为完成添加。
//当队列变空时,消费者将知道
//没有更多的数据。
_samples.CompleteAdding();
}
私有静态void ProcessThread2()
{
int数据;
while(_samples.TryTake(out data,TimeSpan.Infinite,_cancellationTokenSource.Token))
{
//做一些工作。
Console.WriteLine(“处理数据…”);
}
控制台。写入线(“取消”);
}

您仍然需要该输入线程(除非您希望在
Console.KeyAvailable
上旋转一个循环),但这大大简化了您的生产者和消费者。

什么版本的.NET?我使用了.NET async,并按照Jim提到的优化了它们。尽管我在这里给出了控制台的示例,但我必须在WPF应用程序中执行此操作,我正在努力将其移动。谢谢你和吉姆的快速回复。