Domanda

Nella mia domanda precedente, qualcuno aveva significato che l'uso di semafori fosse costoso in C# rispetto all'utilizzo di un monitor. Quindi chiedo questo, come posso sostituire il semaforo in questo codice con un monitor?

Ho bisogno di funzione1 per restituire il suo valore dopo che la funzione2 (in un thread separato) è stata completata. Avevo sostituito il Semaphore.WaitOne con un Monitor.Wait e il Semaphore.Release con un Monitor.PulseAll ma il PulseAll veniva attivato prima del Wait causando il blocco del programma. Qualche idea su come evitare quella condizione di gara?

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();
}
È stato utile?

Soluzione

Puoi farlo con un waithandle invece di un semaforo. Questa sarebbe l'alternativa più semplice e si comporta meglio di un semaforo:

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

Altri suggerimenti

@Reed ha fornito un Soluzione elegante Se devi attendere più thread.

Potresti non voler usare Monitor da questo. Come @Canna Sottolineato, un evento sarebbe sufficiente e fornirebbe la soluzione più pulita e comprensibile che corrisponda ai requisiti del codice.
Il sovraccarico dell'utilizzo delle primitive di sincronizzazione del sistema operativo reale molto probabilmente non avrà importanza nel tuo caso e utilizzando EG Monitor Fornirebbe solo rendimenti decrescenti a costo di complessità molto più elevata.

Detto questo, ecco un'implementazione che utilizza Monitor e segnalazione.

Puoi usare un bool Flag - Guardato dalla serratura - per indicare che hai finito ed evitare di aspettare in quel caso. (UN)
Se inizi davvero un nuovo thread dentro Function2() dove i commenti indicano e usano lock() intorno a Entrambi WaitOne() e Release(), non hai affatto bisogno della bandiera. (B)

A, usando una bandiera:

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, avviare un thread da 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());
    }
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top