我需要一个.NET应用程序中的线程工作者 - .NET有几个类,比如线程池等,但我找不到任何在单个线程上运行的东西,这是我的要求情况下。
所以我自己去写了一篇文章,但是这些事情非常棘手,我确信我有些不对劲。任何人都可以改进它或指向我已经写过的类似的东西吗?
public class ThreadWorker : IDisposable
{
public ThreadWorker()
{
m_Thread = new Thread(ThreadMain);
m_Thread.IsBackground = true;
m_Thread.Name = "Worker Thread";
m_Thread.Start();
}
public void Dispose()
{
if (!m_Terminate)
{
m_Terminate = true;
m_Event.Set();
m_Thread.Join();
}
}
public void QueueUserWorkItem(Action<object> callback, object data)
{
lock (m_Queue) m_Queue.Enqueue(new WorkItem(callback, data));
m_Event.Set();
}
void ThreadMain()
{
while (!m_Terminate)
{
if (m_Queue.Count > 0)
{
WorkItem workItem;
lock (m_Queue) workItem = m_Queue.Dequeue();
workItem.Callback(workItem.Data);
}
else
{
m_Event.WaitOne();
}
}
}
class WorkItem
{
public WorkItem(Action<object> callback, object data)
{
Callback = callback;
Data = data;
}
public Action<object> Callback;
public object Data;
}
AutoResetEvent m_Event = new AutoResetEvent(false);
Queue<WorkItem> m_Queue = new Queue<WorkItem>();
Thread m_Thread;
bool m_Terminate;
}
来吧,撕开它!
请停止询问我是否需要:是的我 - 我有遗留的C代码不是线程安全的,所以我需要在后台线程上同步我的所有调用。
编辑:我的代码的最后一刻更改显然不正确,修复了它。
答案 0 :(得分:4)
我认为你想要这个,因为你不希望任何这些'工作'同时运行,这看起来像是一个有效的要求。但它与“所有在同一个单线程上”略有不同。
(Fx4)任务并行库有一个'ContinuesWith'结构,可以解决类似的问题,但具有不同的接口。
所以我认为你必须(继续)推出自己的。一些批评:
您在锁外检查m_Queue.Count
,这可能是安全的,因为您只有1个消费者,但我会将其折叠到锁中。
此外,您可以使用Monitor.Wait()和Monitor.Pulse()替换AutoResetEvent。这是'更轻'(所有托管代码),并且它与lock(== Monitor.Enter / .Exit)一起运行良好。
答案 1 :(得分:3)
我将忽略你是否应该这样做的问题,只是给你反馈你的代码。大多数是样式问题或使用最佳实践的建议,但其他是需要修复的错误。
m_Terminate
时需要锁定。或者至少它需要是不稳定的。Action<object>
然后传递null作为参数?如果你不想要参数,你不能只使用ThreadStart吗?callback
到QueueUserWorkItem
不为空,以避免NullReferenceException
(fail fast)中的ThreadMain
循环。ObjectDisposedException
被调用后调用QueueUserWorkItem
,您应该抛出Dispose
。答案 2 :(得分:1)
我假设你真的有理由想要一个只有一个线程的线程池。在这种情况下,我只发现一个主要缺陷:当你总是通过Action<T>
时,为什么使用null
?只需使用Action
,它不带任何参数。
答案 3 :(得分:1)
它在其中一条评论中,但BackgroundWorker本质上是一个很好的实现你想要的。虽然你可以开始其中几个,但在你的情况下只需要开始一个。
其次,你实际上可以使用ThreadPool来改变你的策略:
ThreadPool.QueueUserWorkItem(myWorkerProcess);
然后:
void myWorkerProcess(object state) {
WorkItem workItem;
while (!m_Terminate) {
m_Event.WaitOne(5000);
if (m_Queue.Count > 0) {
lock (m_Queue) workItem = m_Queue.Dequeue();
// ... do your single threaded operation here ...
}
}
}
通过这种方式,你只有一个后台线程,它只是循环等着你做你的事。
答案 4 :(得分:1)
使用AutoResetEvent进行同步时,如果对工作项的排队速度超过处理速度,则最终会得到队列中的项目,但没有事件触发项目的处理。我建议使用信号量,以便您可以保持已发出信号的次数,以便处理所有工作项。
答案 5 :(得分:0)
我认为你正在重新发明轮子。提供的System.Threading.ThreadPool提供此功能。我会使用它并回到编写实际的应用程序逻辑。