C# 非常长寿命螺纹的TPL推荐使用方法
我已经阅读了任务并行库的一些MSDN文档,特别是关于TPL的最佳实践用法 我有一个启动线程的应用程序。线程的目的是监视队列并处理已添加的项。队列中项目的处理需要按顺序进行,因此我不希望从队列中同时处理多个项目。线程的寿命与windows进程一样长,仅在应用程序退出时关闭 我想知道使用TPL作为任务启动此后台线程的利弊 我最初的直觉是不使用TPL,因为我将在运行应用程序的整个生命周期中占用线程池线程,这可能会阻止其他任务运行,并干扰线程池的最佳运行 我不确定手动启动一个新的后台线程来执行这项工作会如何影响应用程序其他独立部分中TPL的使用 我想知道在这种情况下,推荐的方法是手动线程还是任务?或者,为了做出明智的选择,我还需要考虑哪些其他因素 我已经包括了下面的代码。请注意,代码中的处理器通常执行CPU绑定操作,然后处理程序通常执行IO绑定操作:C# 非常长寿命螺纹的TPL推荐使用方法,c#,multithreading,queue,task-parallel-library,C#,Multithreading,Queue,Task Parallel Library,我已经阅读了任务并行库的一些MSDN文档,特别是关于TPL的最佳实践用法 我有一个启动线程的应用程序。线程的目的是监视队列并处理已添加的项。队列中项目的处理需要按顺序进行,因此我不希望从队列中同时处理多个项目。线程的寿命与windows进程一样长,仅在应用程序退出时关闭 我想知道使用TPL作为任务启动此后台线程的利弊 我最初的直觉是不使用TPL,因为我将在运行应用程序的整个生命周期中占用线程池线程,这可能会阻止其他任务运行,并干扰线程池的最佳运行 我不确定手动启动一个新的后台线程来执行这项工作会
public class TransactionProcessor : IDisposable
{
private readonly IProcessorConfiguration configuration;
private readonly IList<Tuple<string, IProcessor>> processors = new List<Tuple<string, IProcessor>>();
private readonly IList<Tuple<string, IHandler>> handlers = new List<Tuple<string, IHandler>>();
private AutoResetEvent waitForWork = new AutoResetEvent(true);
private object lockObject = new object();
private bool processThreadShouldExit = false;
private Thread processorThread;
private Queue queue = new Queue();
public TransactionProcessor(IProcessorConfiguration configuration)
{
if (configuration == null)
{
throw new ArgumentNullException("configuration");
}
this.configuration = configuration;
this.Initialise();
}
public void Start()
{
lock (this.lockObject)
{
if (this.processorThread == null)
{
this.processThreadShouldExit = false;
this.processorThread = new Thread(this.Dispatcher);
this.processorThread.Start();
}
}
}
public void Stop()
{
if (this.processorThread != null)
{
this.processThreadShouldExit = true;
this.waitForWork.Set();
this.processorThread.Join();
this.processorThread = null;
}
}
public void QueueTransactionForProcessing(Transaction Transaction, Guid clientID)
{
var queueObject = new QueueObject() { Transaction = Transaction };
lock (this.lockObject)
{
this.queue.Enqueue(queueObject);
}
if (this.waitForWork != null)
{
this.waitForWork.Set();
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (this.processorThread != null)
{
this.Stop();
}
if (this.waitForWork != null)
{
this.waitForWork.Dispose();
this.waitForWork = null;
}
}
private void Dispatcher()
{
if (this.queue.Count == 0)
{
this.waitForWork.Reset();
}
while (!this.processThreadShouldExit)
{
if (this.queue.Count > 0 || this.waitForWork.WaitOne(60000))
{
while (this.queue.Count > 0 && !this.processThreadShouldExit)
{
QueueObject queueObject;
lock (this.lockObject)
{
queueObject = (QueueObject)this.queue.Dequeue();
}
this.ProcessQueueItem(queueObject);
}
if (this.queue.Count == 0)
{
this.waitForWork.Reset();
}
}
}
}
private void ProcessQueueItem(QueueObject item)
{
var correlationId = Guid.NewGuid();
try
{
bool continuePipeline = true;
foreach (var processor in this.processors)
{
processor.Item2.Process(item.Transaction, correlationId, ref continuePipeline);
if (!continuePipeline)
{
break;
}
}
if (continuePipeline)
{
foreach (var handler in this.handlers)
{
Transaction clonedTransaction = item.Transaction.Clone();
try
{
handler.Item2.Handle(clonedTransaction, correlationId);
}
catch (Exception e)
{
}
}
}
}
catch (Exception e)
{
}
}
private void Initialise()
{
foreach (var processor in this.configuration.Processors)
{
try
{
Type processorType = Type.GetType(processor.Value);
if (processorType != null && typeof(IProcessor).IsAssignableFrom(processorType))
{
var processorInstance = (IProcessor)Activator.CreateInstance(processorType);
this.processors.Add(new Tuple<string, IProcessor>(processor.Key, processorInstance));
}
catch (Exception e)
{
}
}
foreach (var handler in this.configuration.Handlers)
{
try
{
Type handlerType = Type.GetType(handler.Value);
if (handlerType != null && typeof(IHandler).IsAssignableFrom(handlerType))
{
var handlerInstance = (IHandler)Activator.CreateInstance(handlerType);
this.handlers.Add(new Tuple<string, IHandler>(handler.Key, handlerInstance));
}
}
catch (Exception e)
{
}
}
}
}
如果您需要使用线程之类的低级构造,那么TPL也可以满足长时间运行的任务,并且使用它可以生成更灵活和可维护的代码 您可以使用以下命令启动长时间运行的任务:
Task.Factory.StartNew(() => {}, TaskCreationOptions.LongRunning);
从MSDN文档:
LongRunning指定任务将长期运行,
粗粒度操作,涉及的组件比
细粒度系统。它向TaskScheduler提供以下提示:
超额认购可能是有保证的。超额订阅允许您创建
线程数超过可用的硬件线程数
因此,调度器可以创建额外的线程,以确保线程池容量足够。另外,手动创建线程不会影响代码其他部分中TPL的使用
就个人而言,我肯定会选择TPL而不是手动创建线程。有一点学习曲线,但它是一个非常强大的库,可以满足各种场景。处理作业是IO绑定作业还是CPU绑定作业?也就是说,你是将一个对象和一些CPU绑定的东西(比如计算)出列,还是通过网络发送数据?另外,我建议您在代码中发布您迄今为止尝试过的内容。嗨,Yuval Itzhakov,该操作基本上是IO绑定的。到目前为止,我只是尝试手动创建线程,这是可行的,但是我想知道切换到使用TPL是否会有任何影响。您能告诉我们您尝试执行的代码吗?我认为你根本不需要使用背景线程。编辑问题以包含代码。这是非常有用的,我不知道你能够告诉API它是否是一个长时间运行的操作,这样它就可以适当地改变它的行为。如果op对他的队列主要进行IO绑定工作,那么他可能根本不需要后台线程。