我创建了一个包含BlockingCollection的简单类。它表示将按接收顺序执行的操作队列。我已经阅读了很多关于TPL的文章,似乎我应该使用它而不是我目前正在使用的那些。一个原因是单元测试会更容易,而且编写的代码也会更少。我知道您可以使用Task.Factory.StartNew()等方式轻松启动新任务,但不确定如何以与我目前的类相似的方式使用它。如何用TPL完成同样的事情?
根据要求,这是我创建的课程:
public class MyService
{
/// <summary>Queue of actions to be consumed on a separate thread</summary>
private BlockingCollection<MyObject> queue = new BlockingCollection<MyObject>();
public MyService()
{
StartService();
}
public void AddToQueue(MyObject newObject)
{
queue.Add(newObject);
}
private void StartService()
{
System.Threading.Tasks.Task.Factory.StartNew(() =>
{
while (true)
{
try
{
MyObject myObject = queue.Take(); // blocks until new object received
// Do work...
}
catch (Exception e)
{
// Log...
}
}
});
}
}
答案 0 :(得分:0)
BlockingCollection
以及为简单的生产者 - 消费者方案创建的异步集合系列。 (例如一位作家和多位读者)
当然 - 您可以设法使用Task.Run
构建几乎相同的内容,将List<T>
添加,删除,清理等项目添加到非同步集合中,但是您必须管理所有多个自己解决问题(并且存在很多问题)。
例如:
public class MyService
{
/// <summary>Queue of actions to be consumed on a separate thread</summary>
private BlockingCollection<MyObject> queue = new BlockingCollection<MyObject>();
private IEnumerable<Task> readers = Enumerable.Range(0, 10).Select((t) => new Task(() => this.StartService()));
public MyService()
{
StartService();
readers.AsParallel().ForAll(t => t.Start());
}
public void AddToQueue(MyObject newObject)
{
queue.Add(newObject);
}
private void StartService()
{
while (true)
{
try
{
MyObject myObject = queue.Take(); // blocks until new object received
// Do work...
}
catch (Exception e)
{
// Log...
}
}
}
}
你看 - 同一个系列上有多个'读者'。如果你自己完成了BlockingCollection
,你应该处理所有lock
周围的集合等。
答案 1 :(得分:0)
旧时尚,阻止同步和基于任务的异步不能很好地混合。
Task.Run(() =>
{
while (true)
{
// some thing that sometimes blocks
}
});
只是一种奇特的写作方式
new Thread(() =>
{
while (true)
{
// some thing that sometimes blocks
}
});
这两个都将永远占据一个主线。第一个将使用线程池中的一个,这应该比特别创建的更好,但是因为它从未在以后发布,所以好处消失了。
如果你想使用任务和TPL,并从中获益,你应该尽可能地避免任何阻塞。例如,您可以使用ConcurrentQueue
作为后备队列,并执行以下操作:
public class MyService
{
/// <summary>Queue of actions to be consumed by separate task</summary>
private ConcurrentQueue<MyObject> queue = new ConcurrentQueue<MyObject>();
private bool _isRunning = false;
private Task _consumingTask;
public MyService()
{
}
public void AddToQueue(MyObject newObject)
{
queue.Add(newObject);
}
private void StartService()
{
_isRunning = true;
Task.Run( async () =>
{
while (_isRunning )
{
MyObject myObject;
while(_isRunning && queue.TryDequeue(out myObject)
{
try
{
// Do work...
}
catch (Exception e)
{
// Log...
}
}
await Task.Delay(100); // tune this value to one pertinent to your use case
}
});
}
public void StopService()
{
_isRunning = false;
_consumingTask.Wait();
}
}
这种实现永远不会阻塞,只在真正需要计算时占用一个线程。
}也很容易将其他Task
与它混合在一起。
TLDR:如果你走Task
方式,那就一直走。中间点真的不是你想要的,你将获得所有的复杂性,没有任何优势。