我有多个线程可以将项目添加到无锁队列中 然后,这些项目由另一个线程处理。
在生产者线程中,我需要启动消费者线程,但前提是它尚未运行或启动。
具体做法是:
public void BeginInvoke(Action method)
{
//This runs on multiple background threads
pendingActions.Enqueue(method);
if (ProcessQueue hasn't been posted)
uiContext.Post(ProcessQueue, null);
}
private void ProcessQueue(object unused)
{
//This runs on the UI thread.
Action current;
while (pendingActions.TryDequeue(out current))
current();
}
我使用的是.Net 3.5,而不是4.0。 :(
答案 0 :(得分:2)
我创建了以下类来执行此操作:
///<summary>Ensures that a block of code is only executed once at a time.</summary>
class Valve
{
int isEntered; //0 means false; 1 true
///<summary>Tries to enter the valve.</summary>
///<returns>True if no other thread is in the valve; false if the valve has already been entered.</returns>
public bool TryEnter()
{
if (Interlocked.CompareExchange(ref isEntered, 1, 0) == 0)
return true;
return false;
}
///<summary>Allows the valve to be entered again.</summary>
public void Exit()
{
Debug.Assert(isEntered == 1);
isEntered = 0;
}
}
我这样用:
readonly Valve valve = new Valve();
public void BeginInvoke(Action method)
{
pendingActions.Enqueue(method);
if (valve.TryEnter())
uiContext.Post(ProcessQueue, null);
}
private void ProcessQueue(object unused)
{
//This runs on the UI thread.
Action current;
while (pendingActions.TryDequeue(out current))
current();
valve.Exit();
}
这种模式安全吗?
有更好的方法吗?
这个班的名字是否更正确?
答案 1 :(得分:2)
最简单的方法是使用Semaphore
。它将具有队列大小的计数。
答案 2 :(得分:1)
这对你有用吗?
volatile int running; //not a boolean to allow ProcessQueue to be reentrant.
private void ProcessQueue(object unused)
{
do
{
++running;
Action current;
while (pendingActions.TryDequeue(out current))
current();
--running;
}
while (pendingActions.Count != 0);
}
public void BeginInvoke(Action method)
{
pendingActions.Enqueue(method);
if (running != 0)
uiContext.Post(ProcessQueue, null);
}
答案 3 :(得分:0)
为使用者线程创建第二个Dispatcher。然后,生产者线程可以使用该调度程序的BeginInvoke()方法将数据发送到使用者线程。 Dispatcher的队列取代pendingActions队列,并确保使用者线程一次只处理一个工作项。
不是让生产者线程尝试协调启动和停止使用者线程,而是在任何生产者启动之前启动消费者线程,让它闲置。 Dispatcher应该在需要时自动将其唤醒。