Domanda

Come sincronizzare correttamente questo? Al momento è possibile che SetData viene richiamato dopo e.WaitOne() ha completato così d potrebbe essere già impostato un altro valore. Ho cercato di inserire serrature ma il risultato in una situazione di stallo.

AutoResetEvent e = new AutoResetEvent(false);

public SetData(MyData d)
{
   this.d=d;
   e.Set();    // notify that new data is available
}

// This runs in separate thread and waits for d to be set to a new value
void Runner() 
{    
   while (true)
   {
      e.WaitOne();  // waits for new data to process
      DoLongOperationWith_d(d);
   }
}

Sarà la soluzione migliore quella di introdurre un nuovo dataAlreadyBeenSetAndWaitingToBeProcessed variabile booleana che è impostata in SetData true e alla fine di DoLongOperationWith_d potrebbe essere impostata su true, quindi se SetData viene chiamato con questo set di variabili al vero potrebbe essere solo il ritorno ?

È stato utile?

Soluzione

Questa non è testato, ma è un modo elegante per fare questo con le primitive .net base:

class Processor<T> {
    Action<T> action;
    Queue<T> queue = new Queue<T>();

    public Processor(Action<T> action) {
        this.action = action;
        new Thread(new ThreadStart(ThreadProc)).Start();
    }

    public void Queue(T data) {
        lock (queue) {
            queue.Enqueue(data);
            Monitor.Pulse(queue); 
        }            
    }

    void ThreadProc() {
        Monitor.Enter(queue);
        Queue<T> copy;

        while (true) {                 
            if (queue.Count == 0) {
                Monitor.Wait(queue);
            }

            copy = new Queue<T>(queue);
            queue.Clear();
            Monitor.Exit(queue);

            foreach (var item in copy) {
                action(item); 
            }

            Monitor.Enter(queue); 
        }
    }
}

Esempio di programma:

class Program {

    static void Main(string[] args) {

        Processor<int> p = new Processor<int>((data) => { Console.WriteLine(data);  });
        p.Queue(1);
        p.Queue(2); 

        Console.Read();

        p.Queue(3);
    }
}

Questa è una versione non in coda, una versione della coda può essere preferito:

object sync = new object(); 
AutoResetEvent e = new AutoResetEvent(false);
bool pending = false; 

public SetData(MyData d)
{
   lock(sync) 
   {
      if (pending) throw(new CanNotSetDataException()); 

      this.d=d;
      pending = true;
   }

   e.Set();    // notify that new data is available
}

void Runner() // this runs in separate thread and waits for d to be set to a new value
{

     while (true)
     {

             e.WaitOne();  // waits for new data to process
             DoLongOperationWith_d(d);
             lock(sync) 
             {
                pending = false; 
             }
     }
}

Altri suggerimenti

Ci sono due scenari preoccupanti forse qui.

1

  • DoLongOperationWith_d (d) finisce.
  • SetData () viene chiamato, la memorizzazione di un nuovo valore di d.
  • e.WaitOne () viene chiamato, ma dal momento che un valore è già stato impostato il thread attende per sempre.

Se questo è la vostra preoccupazione, penso che ci si può rilassare. Dal , vediamo che

  

Se un thread chiama WaitOne mentre l'AutoResetEvent è nello stato segnalato, il filo non bloccare. L'AutoResetEvent rilascia il filo immediatamente e ritorna allo stato non segnalato.

In modo che non è un problema. Tuttavia, a seconda di come e quando si chiama SetData (), si può avere a che fare con la più grave

2

  • SetData () viene chiamato, la memorizzazione di un nuovo valore di d e svegliare il corridore.
  • DoLongOperationWith_d (d) si avvia.
  • SetData () viene chiamato di nuovo, la memorizzazione di un nuovo valore di d.
  • SetData () viene chiamato di nuovo! Il vecchio valore di d è perso per sempre; DoLongOperationWith_d () non potrà mai essere invocato su di esso.

Se questo è il vostro problema, il modo più semplice per risolverlo è una coda concomitante. Implementazioni abbondano.

È possibile utilizzare 2 eventi,

AutoResetEvent e = new AutoResetEvent(false);
AutoResetEvent readyForMore = new AutoResetEvent(true); // Initially signaled

public SetData(MyData d)
{
   // This will immediately determine if readyForMore is set or not.
   if( readyForMore.WaitOne(0,true) ) {
     this.d=d;
     e.Set();    // notify that new data is available
  }
  // you could return a bool or something to indicate it bailed.
}

void Runner() // this runs in separate thread and waits for d to be set to a new value
{

     while (true)
     {

             e.WaitOne();  // waits for new data to process
             DoLongOperationWith_d(d);
             readyForMore.Set();
     }
}

Una delle cose che si possono fare con questo approccio è avere SetData prendere un timeout, e passare che in WaitOne. Credo che comunque si shoudl indagare ThreadPool.QueueUserWorkItem.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top