我有一个简单的生产者 - 消费者类,使用BufferBlock
对象来发布从TPL Dataflow Docs改编的消息,我的问题是在WPF应用程序中使用它导致消费者在同一个主要文件中接收数据-thread,同时控制台应用程序在工作线程中执行它。这是我的代码:
public class DataflowProducerConsumer
{
BufferBlock<int> _buffer;
public DataflowProducerConsumer()
{
_buffer = new BufferBlock<int>();
var consumer = UseBufferBlock(_buffer);
}
public void Produce(int number)
{
_buffer.Post(number);
var actionBlock = UseActionBlock();
actionBlock.Post(number);
}
public async Task UseBufferBlock(ISourceBlock<int> source)
{
while (await source.OutputAvailableAsync())
{
int data = source.Receive();
// ...process data asyncron
Console.WriteLine("BufferBlock-Thread {0} received: {1}", Thread.CurrentThread.ManagedThreadId, data);
}
}
public ITargetBlock<int> UseActionBlock()
{
return new ActionBlock<int>(data =>
{
Console.WriteLine(string.Format("ActionBlock-Thread: {0} received: {1}", Thread.CurrentThread.ManagedThreadId, data));
});
}
}
在主要或点击事件中执行测试:
var obj = new DataflowProducerConsumer();
obj.Produce(19);
好吧,我将Post
更改为SendAsync
但没有成功。 ActionBlock
部分运作良好。有什么不同?我做错了什么?
答案 0 :(得分:1)
这与TPL DataFlow本身无关,而是与以下声明有关:
await source.OutputAvailableAsync()
await将捕获当前SynchronizationContext
并将后续(等待之后的其余方法)发布到它。如果没有同步上下文(如在控制台应用程序中) - 它将继续发布到线程池线程。
WPF具有同步上下文,并且发布到其中的是发布到UI线程(如Dispatcher.BeginInvoke()
),因此您所观察到的内容。
要防止捕获当前同步上下文(以及 - 始终将后续连续发布到线程池线程),请使用ConfigureAwait(false)
:
while (await source.OutputAvailableAsync().ConfigureAwait(false))
{
int data = source.Receive();
// ...process data asyncron
Console.WriteLine("BufferBlock-Thread {0} received: {1}", Thread.CurrentThread.ManagedThreadId, data);
}
另请注意,有ReceiveAsync()
方法,您可以使用(并等待)而不是同步Receive()
,但这与相关问题无关。