简体   繁体   English

三个c#并发队列 - 如何在需要执行特定操作时暂停它们?

[英]three c# concurrent queue's - how can I pause them while a particular action needs to happen?

I have a question, and I could do with some code examples to help me, and I feel it may help to give some background. 我有一个问题,我可以用一些代码示例来帮助我,我觉得它可能有助于提供一些背景知识。

I have the need to create an engine of 3 Queues (in C#, winforms). 我需要创建一个3队列引擎(在C#中,winforms)。 The 3 Queues merely contain an "action" object. 3个队列只包含一个“动作”对象。 Actions get thrown into the engine, and stick themselves to the "most available" Queue (basically, the Queue with the lowest count). 操作被抛入引擎,并将自己粘贴到“最可用”队列(基本上是具有最低计数的队列)。 Almost all of the time the Queues can run discretely and asynchronously with no harm. 几乎所有的队列都可以离散地和异步地运行而不会造成任何伤害。 However there is one "Action" situation which may happen, and when that type of "Action" occurs and does bubble to the front of a Queue, it must : 然而,可能会发生一种“动作”情况,当这种类型的“动作”发生并且确实冒泡到队列的前面时,它必须:

  • wait for the other queues to stop their current actions 等待其他队列停止当前的操作
  • lock/pause them when they are finished on their current Action 当他们完成当前动作时锁定/暂停它们
  • run the Action alone until it finishes 单独运行Action直到它完成
  • release the lock on the other 2 queues. 释放其他2个队列上的锁。

With the added issue that any of the 3 queues can lock the other 2. 增加的问题是3个队列中的任何一个都可以锁定其他2个队列。

Does anyone have any experience of this? 有没有人有这方面的经验?

I hope so, it seems a bit painful :-) Thanks in advance 我希望如此,似乎有点痛苦:-)在此先感谢

First off, I wouldn't suggest using three queues. 首先,我不建议使用三个队列。 I'd suggest using one queue and just have 3 different tasks reading from it. 我建议使用一个队列,只有3个不同的任务从中读取。 I'd also suggest using BlockingCollection<T> (which is just a wrapper for a ConcurrentQueue as it's easier to work with. 我还建议使用BlockingCollection<T> (它只是ConcurrentQueue的包装器,因为它更容易使用。

As for the rest, a ReaderWriterLockSlim ( Thanks Casperah ) should handle it easy enough. 至于其余的, ReaderWriterLockSlim感谢Casperah )应该很容易处理它。 A Writer requires an exclusive lock, and a reader only locks out other writers, which is exactly your use case. Writer需要一个独占锁,而读者只能锁定其他编写器,这正是你的用例。

var queue = new BlockingCollection<Action>();

int numWorkers = 3;
ReaderWriterLockSlim throttler = new ReaderWriterLockSlim();


for (int i = 0; i < numWorkers; i++)
{
    Task.Factory.StartNew(() =>
    {
        foreach (Action nextAction in queue.GetConsumingEnumerable())
        {
            if (mustBeExectutedSerially(nextAction))
            {
                try
                {
                    throttler.EnterWriteLock();
                    nextAction();
                }
                finally
                {
                    throttler.ExitWriteLock();
                }
            }
            else
            {
                try
                {
                    throttler.EnterReadLock();
                    nextAction();
                }
                finally
                {
                    throttler.ExitReadLock();
                }
            }
        }
    });
}

This is a combination of the single queue approach recommended by Servy and the ReaderWriterLock suggestion by Casperah . 这是Servy推荐单队列方法Casperah建议ReaderWriterLock的组合

ReaderWriterLockSlim throttler = new ReaderWriterLockSlim();
for (int i = 0; i < numWorkers; i++)
{
    Task.Factory.StartNew(() =>
    {
        foreach (Action nextAction in queue.GetConsumingEnumerable())
        {
            if (mustBeExectutedSerially(nextAction))
            {
                try
                {
                    throttler.EnterWriteLock();
                    nextAction();
                }
                finally
                {
                    throttler.ExitWriteLock();
                }
            }
            else
            {
                try
                {
                    throttler.EnterReadLock();
                    nextAction();
                }
                finally
                {
                    throttler.ExitReadLock();
                }
            }
        }
    });
}

It seems that a System.Threading.ReaderWriterLock will do the job for you. 似乎System.Threading.ReaderWriterLock将为您完成这项工作。

A normal task should do this: 正常任务应该这样做:

readerWriterLock.AcquireReaderLock(timeout);
try
{
    RunNormalAction();
}
finally
{
     readerWriterLock.ReleaseReaderLock();
}

And the advanced task should do this: 高级任务应该这样做:

readerWriterLock.AcquireWriterLock(timeout);
try
{
    RunSpecialAction();
}
finally
{
     readerWriterLock.ReleaseWriterLock();
}

You can start as many ReaderLocks as you want, and they will keep running as expected. 您可以根据需要启动任意数量的ReaderLocks,它们将按预期继续运行。 When a WriterLock is Acquired all the ReaderLocks has been released and only one WriterLock will run at a time. 获取WriterLock后,所有ReaderLocks都已释放,一次只能运行一个WriterLock。

My humble sugestion: 我谦虚的消化:

Create three objects 创建三个对象

object threadlock1 = new object();
object threadlock2 = new object();
object threadlock3 = new object();

Each thread acquires lock over one object before running any action. 在运行任何操作之前,每个线程都会锁定一个对象。

lock (threadlock1)  // On thread 1, for example
{ //Run Action }

When THE action comes, the thread with THE action must acquire lock over the three objects, thus waiting for the other threads to finish their work, and preventing them from doing any more. 当动作到来时,具有THE动作的线程必须锁定三个对象,从而等待其他线程完成其工作,并阻止它们再做任何操作。

lock (threadlock1)  // On thread 1, for example
{
 lock (threadlock2)
 {
  lock (threadlock3)
  {
    //Run THE Action 
  }
 }
}

When THE action is finished, you release all three locks, and all is back to normal, with each thread holding it's own lock, and resuming actions. 当该动作完成时,释放所有三个锁,并且所有锁都恢复正常,每个线程都拥有自己的锁,并恢复操作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM