我在下面有一个简单的程序,有2个线程执行某项任务。
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.");
}
}
如果用户请求取消或工作完成,我希望程序退出。
我不确定如何在ProcessThread1用完工作时收到通知。目前我在工作完成时设置cancel = true但似乎不对。任何帮助表示赞赏。
答案 0 :(得分:2)
如果您使用Task
而不是手动创建线程,则可以在任务上附加 continuation ,以通知您的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.
请注意,这两者都是在设计时考虑了用户界面 - 并且在控制台应用程序中表现异常,因为控制台应用程序中没有当前的同步上下文。当您将其移动到真正的用户界面时,它将表现正常。
答案 1 :(得分:1)
再启动一个线程,其唯一的工作是等待控制台输入:
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退出。
无需显式终止输入线程。程序退出时会死掉。
顺便说一句,我会从线程1 proc中删除这些行:
// I dont like this. Instead can I get notified in the UI thread that this thread is complete.
_cancel = true;
_cancellationTokenSource.Cancel();
我会完全删除_cancel
变量,并让第一个线程检查IsCancellationRequested
就像第二个线程一样。
不幸的是,您必须启动一个专用线程来等待控制台输入,但这是我知道实现此目的的唯一方法。 Windows控制台似乎没有可等待的事件。
请注意,您可以使用Task
执行相同操作,整体更易于使用。任务执行的代码是相同的。
从更大的角度来看,我发现你有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.");
}
你仍然需要那个输入线程(除非你想在Console.KeyAvailable
上旋转循环),但这大大简化了你的制作人和消费者。