Как я могу убедиться, что ровно один поток что-то сделает? - PullRequest
10 голосов
/ 23 июня 2011

У меня есть несколько потоков, которые добавляют элементы в очередь без блокировки.
Затем элементы обрабатываются другим потоком.

В потоках производителя мне нужно запустить поток потребителя, , но только если он еще не запущен или не запущен .

В частности:

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.(

Ответы [ 4 ]

2 голосов
/ 23 июня 2011

Самый простой способ - использовать Semaphore.У него будет счетчик размера очереди.

2 голосов
/ 23 июня 2011

Я создал следующий класс для этого:

///<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 голос
/ 23 июня 2011

Это у вас работает?

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); 
} 
0 голосов
/ 23 июня 2011

Создайте второй Диспетчер для потока потребителя.Затем потоки производителя могут использовать метод BeginInvoke () этого диспетчера для отправки данных в поток потребителя.Очередь Dispatcher занимает место вашей очереди pendingActions и гарантирует, что поток потребителя обрабатывает только один рабочий элемент за раз.

Вместо того, чтобы потоки производителя пытались координировать запуск и остановку потока потребителя, простозапустите потребительский поток до того, как будут запущены какие-либо производители, и дайте ему бездействовать.Диспетчер должен автоматически позаботиться о его пробуждении при необходимости.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...