我在WPF中有一个生产者 - 消费者应用程序。点击一个按钮后。
private async void Start_Click(object sender, RoutedEventArgs e)
{
try
{
// set up data
var producer = Producer();
var consumer = Consumer();
await Task.WhenAll(producer, consumer);
// need log the results in Summary method
Summary();
}
}
摘要方法是无效的;我认为这是正确的。
private void Summary(){}
async Task Producer(){ await something }
async Task Consumer(){ await something }
编辑:
我的问题是在Summary()
方法中我必须使用任务中的计算值,但Consumer
任务是一个长时间运行的过程。程序快速运行Summary
甚至没有获取更新的值。它使用初始值。
我的想法:
await Task.WhenAll(producer, consumer);
Summary();
EDIT2:11:08 AM 11/05/2014
private void Summary()
{
myFail = 100 - mySuccess;
_dataContext.MyFail = myFail; // update window upon property changed
async Task Consumer()
{
try
{
Dictionary<string, string> dict = new Dictionary<string, string>();
var executionDataflowBlockOptions = new ExecutionDataflowBlockOptions
{
MaxDegreeOfParallelism = 5,
CancellationToken = cToken
};
var c = new ActionBlock<T>(
t=>
{
if (cToken.IsCancellationRequested)
return;
dict = Do(t, cToken);
if(dict["Success"] == "Success")
mySuccess++;
目前的问题是mySuccess
始终是Summary
方法中的初始值。
答案 0 :(得分:0)
生产者和消费者完成后,您可以使用ContinueWith
方法执行Summary
:
Task.WhenAll(producer, consumer)
.ContinueWith(continuation => Summary());
编辑1
您似乎正在滥用或使用错误的生产者/消费者模式。
生产者应该生成值并将它们铲入通信管道的一端。在管道的另一端,消费者在可用时消耗这些值。换句话说,消费者等待让生产者产生一些价值并将价值放在管道中,并使价值到达管道的末端。
通常这涉及某种信令机制,其中生成者在创建值时发出信号(唤醒)消费者。
在您的情况下,您没有信号机制,我强烈怀疑您的生产者只生成一个值。如果是后一种情况,您只需从&#34;生产者&#34;。
返回一个值但是,如果您的生产者正在创建多个值,则可以使用BlockingCollection<T>
类将值从生产者发送到消费者。
在Producer
课程中,获取对管道的引用并将数据放入其中:
public class Producer
{
private BlockingCollection<Data> _pipe;
public void Start()
{
while(!done)
{
var value = ProduceValue();
_pipe.Add(value);
}
// Signal the consumer that we're finished
_pipe.CompleteAdding();
}
}
在Consumer
类中等待值到达并处理每个值:
public class Consumer
{
private BlockingCollection<Data> _pipe;
public void Start()
{
foreach(var value in _pipe.GetConsumingEnumerable())
{
// GetConsumingEnumerable will block until a value arrives and
// will exit when producer calls CompleteAdding()
Process(value);
}
}
}
完成上述操作后,您可以使用ContinueWith
方法await
或WhenAll
来运行Summary
。
编辑2
正如评论中所承诺的,我已经分析了您在MSDN Forum上发布的代码。代码中有几个问题。
首先,最简单的解决方法是,您不以线程安全的方式递增计数器。增量(value++
)不是原子操作,因此在增加共享字段时应该小心。一个简单的方法是:
Interlocked.Increment(ref evenNumber);
现在,代码中存在实际问题:
正如我前面提到的,消费者不知道生产者何时完成了产生价值。因此,在生产者退出for
块之后,它应该表示它已经完成。消费者等待生产者的结束信号;否则它将永远等待下一个值,但不会成为一个。
您正在将BufferBlock
与开始执行的消费者代码相关联,但您并未等待消费者群体完成 - 您只需等待0.5秒钟退出使用者方法,让消费者块的工作线程徒劳地完成工作。
由于上述原因,您的Report
方法在处理完成之前执行,在方法执行时输出evenNumber
计数器的值,而不是在所有处理完成时
以下是带有一些注释的已编辑代码:
class Program
{
public static BufferBlock<int> m_Queue = new BufferBlock<int>(new DataflowBlockOptions { BoundedCapacity = 1000 });
private static int evenNumber;
static void Main(string[] args)
{
var producer = Producer();
var consumer = Consumer();
Task.WhenAll(producer, consumer).Wait();
Report();
}
static void Report()
{
Console.WriteLine("There are {0} even numbers", evenNumber);
Console.Read();
}
static async Task Producer()
{
for (int i = 0; i < 500; i++)
{
// Send a value to the consumer and wait for the value to be processed
await m_Queue.SendAsync(i);
}
// Signal the consumer that there will be no more values
m_Queue.Complete();
}
static async Task Consumer()
{
var executionDataflowBlockOptions = new ExecutionDataflowBlockOptions
{
MaxDegreeOfParallelism = 4
};
var consumerBlock = new ActionBlock<int>(x =>
{
int j = DoWork(x);
if (j % 2 == 0)
// Increment the counter in a thread-safe way
Interlocked.Increment(ref evenNumber);
}, executionDataflowBlockOptions);
// Link the buffer to the consumer
using (m_Queue.LinkTo(consumerBlock, new DataflowLinkOptions { PropagateCompletion = true }))
{
// Wait for the consumer to finish.
// This method will exit after all the data from the buffer was processed.
await consumerBlock.Completion;
}
}
static int DoWork(int x)
{
Thread.Sleep(100);
return x;
}
}