2013-04-11 6 views
12

Ho visto numerose altre domande simili a questo ma non ho trovato la mia risposta lì.Come forzare Task.Factory.StartNew in un thread in background?

mio problema era che stavo creando le discussioni con il seguente flusso:

private void btn_Click(object sender, EventArgs e) 
{ 
    service.GetCount(
     (count, ex) => 
     { 
      if (ex != null) 
       return; 

      for (int i = 0; i < count; i++) 
      { 
       service.Get(onItemReceived, i); 
      } 
     } 
    ); 
} 

public void GetCount(Action<int, Exception> callback) 
{ 
    var callingThread = TaskScheduler.FromCurrentSynchronizationContext(); 

    Func<int> action =() => 
    { 
     return client.GetCount(); // Synchronous method, could take a long time 
    }; 

    Action<Task<int>> completeAction = (task) => 
    { 
     Exception ex = (task.Exception != null) ? task.Exception.InnerException : task.Exception; 

     if (callback != null) 
      callback(task.Result, ex); 
    }; 

    Task.Factory.StartNew(action).ContinueWith(completeAction, callingThread); 
} 

public void Get(Action<object, Exception> callback, int index) 
{ 
    var callingThread = TaskScheduler.FromCurrentSynchronizationContext(); 

    Func<object> action =() => 
    { 
     return client.Get(index); // Synchronous method, could take a long time 
    }; 

    Action<Task<object>> completeAction = (task) => 
    { 
     Exception ex = (task.Exception != null) ? task.Exception.InnerException : task.Exception; 

     if (callback != null) 
      callback(task.Result, ex); 
    }; 

    Task.Factory.StartNew(action).ContinueWith(completeAction, callingThread); 
} 

In questo modo, ciascuno dei metodi asincroni del mio servizio sarebbe richiamata il filo essi sono stati inizialmente chiamati (in genere il thread UI). Quindi sto simulando come funzionano le parole chiave attendi/asincrone (non posso usare .NET 4.5).

Il problema con questo modello è che ottengo inspiegabilmente bloccato sul thread dell'interfaccia utente dopo la prima chiamata a "ContinueWith". In questo caso, se provo a generare 5 thread per ogni processo, una funzione sincrona Ottieni, verranno eseguiti 1 per 1 anziché in parallelo e bloccheranno il thread dell'interfaccia utente mentre lo faccio, anche se provo a specificare TaskCreationOptions.LongRunning .

Questo non accade mai con la mia prima chiamata a Task.Factory.StartNew, succede solo alle chiamate successive dall'interno del primo callback.

+0

Possibile duplicato di [Come garantire che venga creato un nuovo thread quando si utilizza il metodo Task.StartNew] (https://stackoverflow.com/questions/8039936/how-to-guarantee-a-new-thread-is- created-when-using-the-task-startnew-method) –

risposta

23

Al fine di forzare il lancio di un nuovo thread, è necessario specificare TaskScheduler.Default nella chiamata a Task.Factory.StartNew come segue:

Task.Factory.StartNew(action, 
         CancellationToken.None, 
         TaskCreationOptions.None, 
         TaskScheduler.Default).ContinueWith(completeAction); 

Nel mio test non è necessario specificare TaskCreationOptions .LongRunning per forzare un thread in background, sebbene non dovrebbe ferire.

+0

'LongRunning' impone la creazione di un * thread di pool non thread * completamente nuovo *. – Servy

+2

@Servy: non impone nulla, è un _hint_ per lo scheduler. –

+0

@HenkHolterman In teoria, sì, è corretto. In pratica, nell'attuale implementazione, si crea un nuovo thread. Mentre sono d'accordo, potrebbe essere meglio presumere che sia possibile che non lo sia, dovresti assumere che contrassegnare un'attività come a lungo in esecuzione quando non lo è realmente consumerà risorse aggiuntive. – Servy