138 votes

Retourner une valeur à partir d'un thread ?

Comment retourner une valeur à partir d'un thread ?

122voto

Brian Gideon Points 26683

L'une des façons les plus simples d'obtenir une valeur de retour d'un thread est d'utiliser des fermetures. Créez une variable qui contiendra la valeur de retour du thread, puis capturez-la dans une expression lambda. Attribuez la valeur de retour à cette variable à partir du fil de travail et une fois que ce fil se termine, vous pouvez l'utiliser à partir du fil parent.

void Main()
{
  object value = null; // Used to store the return value
  var thread = new Thread(
    () =>
    {
      value = "Hello World"; // Publish the return value
    });
  thread.Start();
  thread.Join();
  Console.WriteLine(value); // Use the return value here
}

54voto

Igor Bendrup Points 1350

Cela dépend de la manière dont vous voulez créer le fil et de la version .NET disponible :

.NET 2.0+ :

A) Vous pouvez créer le Thread directement l'objet. Dans ce cas, vous pouvez utiliser la "fermeture" - déclarer une variable et la capturer en utilisant une expression lambda :

object result = null;
Thread thread = new System.Threading.Thread(() => { 
    //Some work...
    result = 42; });
thread.Start();
thread.Join();
Console.WriteLine(result);

B) Vous pouvez utiliser les délégués et IAsyncResult et la valeur de retour de EndInvoke() méthode :

delegate object MyFunc();
...
MyFunc x = new MyFunc(() => { 
    //Some work...
    return 42; });
IAsyncResult asyncResult = x.BeginInvoke(null, null);
object result = x.EndInvoke(asyncResult);

C) Vous pouvez utiliser BackgroundWorker la classe. Dans ce cas, vous pourriez utiliser une variable capturée (comme avec Thread ) ou manipuler RunWorkerCompleted événement :

BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += (s, e) => {
    //Some work...
    e.Result = 42;
};
worker.RunWorkerCompleted += (s, e) => {
    //e.Result "returned" from thread
    Console.WriteLine(e.Result);
};
worker.RunWorkerAsync();

.NET 4.0+ :

À partir de .NET 4.0, vous pouvez utiliser Bibliothèque parallèle des tâches y Task classe pour commencer vos fils. Classe générique Task<TResult> vous permet d'obtenir la valeur de retour de Result propriété :

//Main thread will be blocked until task thread finishes
//(because of obtaining the value of the Result property)
int result = Task.Factory.StartNew(() => {
    //Some work...
    return 42;}).Result;

.NET 4.5+ :

À partir de .NET 4.5, vous pouvez également utiliser async / await pour retourner la valeur de la tâche directement au lieu d'obtenir la valeur de la tâche. Result propriété :

int result = await Task.Run(() => {
    //Some work...
    return 42; });

Note : la méthode, qui contient le code ci-dessus, doit être marquée avec le symbole async mot-clé.

Pour de nombreuses raisons, l'utilisation de la bibliothèque parallèle des tâches est la meilleure façon de travailler avec les threads.

34voto

Eric J. Points 73338

J'utiliserais le Travailleur d'arrière-plan et renvoyer le résultat dans e.Result.

EDITAR:

Cette fonction est généralement associée à WinForms et WPF, mais elle peut être utilisée par tout type d'application .NET. Voici un exemple de code pour une application console qui utilise BackgroundWorker :

using System;
using System.Threading;
using System.ComponentModel;
using System.Collections.Generic;
using System.Text;

namespace BGWorker
{
    class Program
    {
        static bool done = false;

        static void Main(string[] args)
        {
            BackgroundWorker bg = new BackgroundWorker();
            bg.DoWork += new DoWorkEventHandler(bg_DoWork);
            bg.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bg_RunWorkerCompleted);
            bg.RunWorkerAsync();

            while (!done)
            {
                Console.WriteLine("Waiting in Main, tid " + Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(100);
            }
        }

        static void bg_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            Console.WriteLine("Completed, tid " + Thread.CurrentThread.ManagedThreadId);
            done = true;
        }

        static void bg_DoWork(object sender, DoWorkEventArgs e)
        {
            for (int i = 1; i <= 5; i++)
            {
                Console.WriteLine("Work Line: " + i + ", tid " + Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(500);
            }
        }
    }
}

Sortie :

Waiting in Main, tid 10
Work Line: 1, tid 6
Waiting in Main, tid 10
Waiting in Main, tid 10
Waiting in Main, tid 10
Waiting in Main, tid 10
Waiting in Main, tid 10
Work Line: 2, tid 6
Waiting in Main, tid 10
Waiting in Main, tid 10
Waiting in Main, tid 10
Waiting in Main, tid 10
Waiting in Main, tid 10
Work Line: 3, tid 6
Waiting in Main, tid 10
Waiting in Main, tid 10
Waiting in Main, tid 10
Waiting in Main, tid 10
Waiting in Main, tid 10
Work Line: 4, tid 6
Waiting in Main, tid 10
Waiting in Main, tid 10
Waiting in Main, tid 10
Waiting in Main, tid 10
Work Line: 5, tid 6
Waiting in Main, tid 10
Waiting in Main, tid 10
Waiting in Main, tid 10
Waiting in Main, tid 10
Waiting in Main, tid 10
Waiting in Main, tid 10
Completed, tid 6

2014 UPDATE

Voir la réponse de @Roger ci-dessous.

https://stackoverflow.com/a/24916747/141172

Il souligne que vous pouvez utiliser une tâche qui renvoie un Task<T> et vérifier Task<T>.Result .

21voto

Reed Copsey Points 315315

Un fil n'est pas une méthode - normalement, on ne "renvoie" pas une valeur.

Toutefois, si vous essayez de récupérer une valeur à partir des résultats d'un traitement, vous disposez de nombreuses options, les deux principales étant les suivantes :

  • Vous pouvez synchroniser un élément de données partagé et le définir de manière appropriée.
  • Vous pouvez également renvoyer les données dans une sorte de rappel.

Cela dépend vraiment de la façon dont vous créez le fil, de l'usage que vous voulez en faire, ainsi que du langage/du cadre/des outils que vous utilisez.

19voto

yogihosting Points 1845

Il suffit d'utiliser l'approche par délégation.

int val;
Thread thread = new Thread(() => { val = Multiply(1, 2); });
thread.Start();

Maintenant, créez la fonction Multiply qui fonctionnera sur un autre fil :

int Multiply(int x, int y)
{
    return x * y;
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X