C# 我如何才能确保只有一个线程可以执行某些操作?

C# 我如何才能确保只有一个线程可以执行某些操作?,c#,.net,multithreading,thread-safety,C#,.net,Multithreading,Thread Safety,我有多个线程将项目添加到无锁队列。 这些项目然后由另一个线程处理 在生产者线程中,我需要启动消费者线程,但前提是它尚未运行或启动 具体而言: public void BeginInvoke(Action method) { //This runs on multiple background threads pendingActions.Enqueue(method); if (ProcessQueue hasn't been posted) uiCont

我有多个线程将项目添加到无锁队列。
这些项目然后由另一个线程处理

在生产者线程中,我需要启动消费者线程,但前提是它尚未运行或启动

具体而言:

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();
}

我使用的是.NET3.5,而不是4.0:(

为此,我创建了以下类:

///<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;
    }
}
这种模式安全吗?
有更好的方法吗?

类是否有更正确的名称?

最简单的方法是使用
信号量。它将具有队列大小计数。

为使用者线程创建第二个。然后,生产者线程可以使用该调度器的BeginInvoke()方法将数据发送到使用者线程。Dispatcher的队列将取代pendingActions队列,并确保使用者线程一次只处理一个工作项


与其让生产者线程尝试协调启动和停止消费者线程,不如在任何生产者启动之前启动消费者线程,并让其处于空闲状态。调度器应在需要时自动将其唤醒。

这对您有用吗

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); 
} 


(我这样做是因为对
SynchronizationContext.Post
的100K调用最终会终止Windows消息队列)我很好奇为什么制作人有责任启动消费者?@Conrad:消费者是在UI线程上运行的,但只有在有项目需要处理的情况下才运行。如果是这样,那么问题和标题的措词就有点混乱。它们让人觉得消费者是在专用线程上运行的。@Sean:我需要准确的答案一个线程启动使用者–调用
uiContext.Post(ProcessQueue,null);
No;我根本不想阻止。如果使用者无事可做,它应该退出。它正在UI线程上运行。@SLaks♦ 没问题!基本上信号量是线程安全计数器。您可以使用WaitOne(0)来不阻止它并检查返回值。但是,我仍然使用.Net 3.5,无法使用
信号量lim
,因此我怀疑我的解决方案更快。@SLaks♦ 当然,你的速度应该更快。但问题是要快多少。@SLaks知道你故意避免使用Monitor.TryEnter。你确定这值得吗?我刚刚意识到我误解了
CompareExchange
的返回值。我不需要唯一的标记。@SLaks因此,如果我理解正确,如果多个线程在EMPT调用“<代码>开始调用< /COD> >,将成功调用<代码> UCONTICE。POST < /C> >其余的将被忽略。@康拉德:完全正确。<代码> PurralQue2/<代码>将将所有的项目都排好。@该解决方案可能会使您的队列被锁定。考虑当所有项目都已被排队和<代码>时,当循环时会发生什么情况?结束,但就在
valve.Exit
被称为某个东西进入队列,并且
valve.TryEnter
失败,在队列中至少留下一项,但没有发布
ProcessQueue
的信号。这是我正在替换的模式(除了WinForms而不是WPF)。我收到了太多的rapid fire消息,以至于事件队列已满。啊哈,明白了。我刚刚通过升级到.NET 4来处理类似的情况:(因此,我想知道的下一件事是,如果你遇到这种情况,那么可能一点阻塞实际上是你的朋友?毕竟,如果他们自然倾向于以比消费者能够消费它更快的速度生成数据,那么在没有任何阻塞机制的情况下,消费者的输入队列的大小将趋于无穷大。我的队列应该是可伸缩性优于Windows消息队列;我会在运行它时发现。Dispatcher使用它自己的队列实现而不是Windows消息队列,虽然我没有运行直接比较,但我确实觉得它的性能似乎更好。在将时间投入自定义解决方案之前,不妨尝试一下。不,因为
SynchronizationContext.Post
是异步的,因此在运行
ProcessQueue
之前,它可能会连续调用
Post
两次。关键是,如果您正在排队100k个帖子,那么可以安全地假设consummr线程比producer线程慢得多。因此,大部分时间都花在消费模式上。因此,如果我禁用po在消费的同时,我会减少数量级的额外帖子数量。是的,这正是我想要做的。
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); 
}