Как я могу заменить этот семафор на монитор? - PullRequest
4 голосов
/ 02 апреля 2010

В моем предыдущем вопросе кто-то имел в виду, что использование семафоров в C # обходится дороже по сравнению с использованием монитора. Поэтому я спрашиваю, как я могу заменить семафор в этом коде на монитор?

Мне нужно, чтобы функция 1 возвращала свое значение после завершения функции 2 (в отдельном потоке). Я заменил Semaphore.WaitOne на Monitor.Wait и Semaphore.Release на Monitor.PulseAll, но PulseAll срабатывал до Wait, вызывая зависание программы. Есть идеи, как избежать этого состояния гонки?

Semaphore semaphore = new Semaphore(0,1);
byte b;
public byte Function1()
{
    // new thread starting in Function2;

    semaphore.WaitOne();
    return b;
}

public void Function2()
{
    // do some thing
    b = 0;
    semaphore.Release();
}

Ответы [ 2 ]

9 голосов
/ 02 апреля 2010

Вы можете сделать это с помощью WaitHandle вместо семафора. Это будет самая простая альтернатива, и она будет работать лучше, чем семафор:

ManualResetEvent manualResetEvent = new ManualResetEvent(false);
byte b;
public byte Function1()
{
    // new thread starting in Function2;

    manualResetEvent.WaitOne();
    return b;
}

public void Function2()
{
    // do some thing
    b = 0;
    manualResetEvent.Set();
}
2 голосов
/ 02 апреля 2010

@ Рид предоставил элегантное решение , если вам нужно дождаться нескольких потоков.

Возможно, вы не захотите использовать Monitor для этого. Как указывало @ Reed , достаточно было бы событие, которое обеспечило бы самое чистое и понятное решение, соответствующее требованиям вашего кода.
Затраты на использование реальных примитивов синхронизации операционной системы, скорее всего, не будут иметь значения в вашем случае, например, при использовании, например, Monitor даст только убывающую отдачу за счет гораздо более высокой сложности.

С учетом сказанного, вот реализация, использующая Monitor и сигнализацию.

Вы можете использовать флаг bool - защищенный замком - чтобы указать, что вы закончили и избежать ожидания в этом случае. (А)
Если вы действительно начинаете новую тему в Function2(), где комментарии указывают и используют lock() вокруг и WaitOne(), и Release(), то флаг вам вообще не нужен. (В)

A, используя флаг:

class Program
{
    static object syncRoot = new object();
    //lock implies a membar, no need for volatile here.
    static bool finished = false;
    static byte b;

    public static byte Function1()
    {
        lock (syncRoot)
        {
            //Wait only if F2 has not finished yet.
            if (!finished)
            {
                Monitor.Wait(syncRoot);
            }
        }
        return b;
    }

    static public void Function2()
    {
        // do some thing
        b = 1;
        lock (syncRoot)
        {
            finished = true;
            Monitor.Pulse(syncRoot);
        }
    }

    static void Main(string[] args)
    {
        new Thread(Function2).Start();
        Console.WriteLine(Function1());
    }
}

B, начало потока с Function1:

class Program
{

    static object syncRoot = new object();
    static byte b;

    public static byte Function1()
    {
        lock (syncRoot)
        {
            // new thread starting in Function2;
            new Thread(Function2).Start();
            Monitor.Wait(syncRoot);
        }
        return b;
    }

    static public void Function2()
    {
        // do some thing
        b = 1;
        //We need to take the lock here as well
        lock (syncRoot)
        {
            Monitor.Pulse(syncRoot);
        }
    }

    static void Main(string[] args)
    {
        Console.WriteLine(Function1());
    }
}
...