¿Devolver un valor del hilo?

Respuestas:

94

Una de las formas más sencillas de obtener un valor de retorno de un hilo es utilizar cierres. Cree una variable que contendrá el valor de retorno del hilo y luego capturelo en una expresión lambda. Asigne el valor de "retorno" a esta variable desde el hilo de trabajo y luego, una vez que ese hilo termine, puede usarlo desde el hilo principal.

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
}
Brian Gideon
fuente
3
¿No lock(value) { value = "Hello world"; }sería mejor manejar la escritura de valores de múltiples hilos?
suma de comprobación
4
@checksum: En este caso particular, no es necesario porque no se valueestán realizando lecturas ni escrituras al mismo tiempo. Pero, sí, siempre tenga en cuenta cuándo es necesario un candado.
Brian Gideon
¡Idea fantástica! Funciona de manera brillante y debería ser la respuesta aceptada.
MerseyViking
34

Depende de cómo desee crear el hilo y la versión de .NET disponible:

.NET 2.0+:

A) Puede crear el Threadobjeto directamente. En este caso, podría usar "cierre" - declarar variable y capturarla usando expresión lambda:

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

B) Puede usar delegados y un IAsyncResultvalor de retorno del EndInvoke()método:

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

C) Puedes usar la BackgroundWorkerclase. En este caso, puede usar la variable capturada (como con el Threadobjeto) o manejar el RunWorkerCompletedevento:

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+:

A partir de .NET 4.0, puede usar Task Parallel Library y Taskclass para iniciar sus hilos. La clase genérica le Task<TResult>permite obtener el valor de retorno de la Resultpropiedad:

//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+:

A partir de .NET 4.5, también puede usar async/ awaitkeywords para devolver el valor de la tarea directamente en lugar de obtener la Resultpropiedad:

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

Nota: el método que contiene el código anterior debe estar marcado con la asyncpalabra clave.

Por muchas razones, el uso de Task Parallel Library es una forma preferible de trabajar con subprocesos.

Igor Bendrup
fuente
33

Usaría el enfoque BackgroundWorker y devolvería el resultado en e.Result.

EDITAR:

Esto se asocia comúnmente con WinForms y WPF, pero puede ser utilizado por cualquier tipo de aplicación .NET. A continuación, se muestra un código de muestra para una aplicación de consola que usa 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);
            }
        }
    }
}

Salida:

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

ACTUALIZACIÓN 2014

Vea la respuesta de @ Roger a continuación.

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

Señala que puede usar una Tarea que devuelva un Task<T>, y compruebe Task<T>.Result.

Eric J.
fuente
Sí, pero solo se aplica a WinForms y WPF.
Henk Holterman
@Henk: No es cierto. Acabo de escribir una aplicación de consola simple que usa BackgroundWorker solo para asegurarme :-) Edité mi publicación con ese código.
Eric J.
Eric, pon algunas líneas de escritura en tu código para ver cuándo sucede y en qué ThreadId. Puede que no salga como esperabas. (Completed se ejecutará antes de que Dowork finalice, y no en el hilo principal). El Bgw necesita un MessagePump.
Henk Holterman
@Henk: Tienes razón a medias. Completed se ejecuta en el mismo subproceso que BackgroundWorker, pero se ejecuta después de que se completa DoWork. Ver salida en respuesta editada.
Eric J.
2
No hay condición de carrera, porque exactamente un subproceso establece la variable y exactamente un subproceso la lee, y el orden exacto de configuración frente a lectura no importa para la ejecución correcta del código (es decir, la condición de terminación puede ocurrir en el subproceso principal un poco antes o después, según el orden en el que estén programados los hilos, pero de cualquier manera obtendrá un resultado correcto).
Eric J.
21

Un hilo no es un método, normalmente no "devuelve" un valor.

Sin embargo, si está tratando de recuperar un valor de los resultados de algún procesamiento, tiene muchas opciones, las dos principales son:

  • Puede sincronizar un dato compartido y configurarlo de forma adecuada.
  • También puede devolver los datos en alguna forma de devolución de llamada.

Realmente depende de cómo esté creando el hilo y cómo quiera usarlo, así como del lenguaje / marco / herramientas que esté usando.

Reed Copsey
fuente
15

Mi clase favorita, ejecuta cualquier método en otro hilo con solo 2 líneas de código.

class ThreadedExecuter<T> where T : class
{
    public delegate void CallBackDelegate(T returnValue);
    public delegate T MethodDelegate();
    private CallBackDelegate callback;
    private MethodDelegate method;

    private Thread t;

    public ThreadedExecuter(MethodDelegate method, CallBackDelegate callback)
    {
        this.method = method;
        this.callback = callback;
        t = new Thread(this.Process);
    }
    public void Start()
    {
        t.Start();
    }
    public void Abort()
    {
        t.Abort();
        callback(null); //can be left out depending on your needs
    }
    private void Process()
    {
        T stuffReturned = method();
        callback(stuffReturned);
    }
}

uso

    void startthework()
    {
        ThreadedExecuter<string> executer = new ThreadedExecuter<string>(someLongFunction, longFunctionComplete);
        executer.Start();
    }
    string someLongFunction()
    {
        while(!workComplete)
            WorkWork();
        return resultOfWork;
    }
    void longFunctionComplete(string s)
    {
        PrintWorkComplete(s);
    }

Tenga en cuenta que longFunctionComplete NO se ejecutará en el mismo hilo que starthework.

Para los métodos que toman parámetros, siempre puede usar cierres o expandir la clase.

Erik
fuente
3
No está claro para todos ... ¿stuffReturned ?, resultOfWork, PrintWorkComplete? etc.
Lost_In_Library
14

Aquí hay un ejemplo simple usando un delegado ...

void Main()
{
   DoIt d1 = Doer.DoThatThang;
   DoIt d2 = Doer.DoThatThang;

   IAsyncResult r1 = d1.BeginInvoke( 5, null, null );
   IAsyncResult r2 = d2.BeginInvoke( 10, null, null );

   Thread.Sleep( 1000 );

   var s1 = d1.EndInvoke( r1 );
   var s2 = d2.EndInvoke( r2 );

   s1.Dump(); // You told me 5
   s2.Dump(); // You told me 10
}

public delegate string DoIt( int x );

public class Doer
{
  public static string DoThatThang( int x  )
  {
    return "You told me " + x.ToString();
  }
}

Hay una serie excelente sobre subprocesos en Threading en C # .

JP Alioto
fuente
9

Simplemente use el enfoque de delegado.

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

Ahora haga la función Multiplicar que funcionará en otro hilo:

int Multiply(int x, int y)
{
    return x * y;
}
yogui
fuente
3
¿Por qué esta respuesta a continuación es "guárdelo en un archivo de texto y recupérelo"?
Jon
7

Me encontré con este hilo cuando también intentaba obtener el valor de retorno de un método que se ejecuta dentro de un hilo. Pensé en publicar mi solución que funciona.

Esta solución utiliza una clase para almacenar tanto el método a ejecutar (indirectamente) como el valor devuelto. La clase se puede utilizar para cualquier función y cualquier tipo de retorno. Simplemente crea una instancia del objeto usando el tipo de valor de retorno y luego pasa la función para llamar a través de un lambda (o delegado).


Implementación de C # 3.0


public class ThreadedMethod<T>
{

    private T mResult;
    public T Result 
    {
        get { return mResult; }
        private set { mResult = value; }
    }

    public ThreadedMethod()
    {
    }

    //If supporting .net 3.5
    public void ExecuteMethod(Func<T> func)
    {
        Result = func.Invoke();
    }

    //If supporting only 2.0 use this and 
    //comment out the other overload
    public void ExecuteMethod(Delegate d)
    {
        Result = (T)d.DynamicInvoke();
    }
}

Para usar este código, puede usar un Lambda (o un delegado). Aquí está el ejemplo usando lambdas:

ThreadedMethod<bool> threadedMethod = new ThreadedMethod<bool>();
Thread workerThread = new Thread((unused) => 
                            threadedMethod.ExecuteMethod(() => 
                                SomeMethod()));
workerThread.Start();
workerThread.Join();
if (threadedMethod.Result == false) 
{
    //do something about it...
}

Implementación de VB.NET 2008


Cualquiera que use VB.NET 2008 no puede usar lambdas con métodos de devolución sin valor. Esto afecta a la ThreadedMethodclase, así que haremos que ExecuteMethoddevuelva el valor de la función. Esto no duele nada.

Public Class ThreadedMethod(Of T)

    Private mResult As T
    Public Property Result() As T
        Get
            Return mResult
        End Get
        Private Set(ByVal value As T)
            mResult = value
        End Set
    End Property

    Sub New()
    End Sub

    'If supporting .net 3.5'
    Function ExecuteMethod(ByVal func As Func(Of T)) As T
        Result = func.Invoke()
        Return Result
    End Function

    'If supporting only 2.0 use this and' 
    'comment out the other overload'
    Function ExecuteMethod(ByVal d As [Delegate]) As T
        Result = DirectCast(d.DynamicInvoke(), T)
        Return Result
    End Function

End Class
Mate
fuente
7

Con el último .NET Framework, es posible devolver un valor de un subproceso separado usando una Tarea, donde la propiedad Result bloquea el subproceso que llama hasta que finaliza la tarea:

  Task<MyClass> task = Task<MyClass>.Factory.StartNew(() =>
  {
      string s = "my message";
      double d = 3.14159;
      return new MyClass { Name = s, Number = d };
  });
  MyClass test = task.Result;

Para obtener más información, consulte http://msdn.microsoft.com/en-us/library/dd537613(v=vs.110).aspx

user8128167
fuente
5

Los delegados de ThreadStart en C # que se usan para iniciar los subprocesos tienen el tipo de retorno 'void'.

Si desea obtener un 'valor de retorno' de un hilo, debe escribir en una ubicación compartida (de una manera adecuada y segura para el hilo) y leer desde allí cuando el hilo haya terminado de ejecutarse.

jscharf
fuente
5

Si no desea usar un BackgroundWorker y solo usa un Thread normal, puede activar un evento para devolver datos como este:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;

namespace ThreadWithDataReturnExample
{
    public partial class Form1 : Form
    {
        private Thread thread1 = null;

        public Form1()
        {
            InitializeComponent();

            thread1 = new Thread(new ThreadStart(this.threadEntryPoint));
            Thread1Completed += new AsyncCompletedEventHandler(thread1_Thread1Completed);
        }

        private void startButton_Click(object sender, EventArgs e)
        {
            thread1.Start();
            //Alternatively, you could pass some object
            //in such as Start(someObject);
            //With apprioriate locking, or protocol where
            //no other threads access the object until
            //an event signals when the thread is complete,
            //any other class with a reference to the object 
            //would be able to access that data.
            //But instead, I'm going to use AsyncCompletedEventArgs 
            //in an event that signals completion
        }

        void thread1_Thread1Completed(object sender, AsyncCompletedEventArgs e)
        {
            if (this.InvokeRequired)
            {//marshal the call if we are not on the GUI thread                
                BeginInvoke(new AsyncCompletedEventHandler(thread1_Thread1Completed),
                  new object[] { sender, e });
            }
            else
            {
                //display error if error occurred
                //if no error occurred, process data
                if (e.Error == null)
                {//then success

                    MessageBox.Show("Worker thread completed successfully");
                    DataYouWantToReturn someData = e.UserState as DataYouWantToReturn;
                    MessageBox.Show("Your data my lord: " + someData.someProperty);

                }
                else//error
                {
                    MessageBox.Show("The following error occurred:" + Environment.NewLine + e.Error.ToString());
                }
            }
        }

        #region I would actually move all of this into it's own class
            private void threadEntryPoint()
            {
                //do a bunch of stuff

                //when you are done:
                //initialize object with data that you want to return
                DataYouWantToReturn dataYouWantToReturn = new DataYouWantToReturn();
                dataYouWantToReturn.someProperty = "more data";

                //signal completion by firing an event
                OnThread1Completed(new AsyncCompletedEventArgs(null, false, dataYouWantToReturn));
            }

            /// <summary>
            /// Occurs when processing has finished or an error occurred.
            /// </summary>
            public event AsyncCompletedEventHandler Thread1Completed;
            protected virtual void OnThread1Completed(AsyncCompletedEventArgs e)
            {
                //copy locally
                AsyncCompletedEventHandler handler = Thread1Completed;
                if (handler != null)
                {
                    handler(this, e);
                }
            }
        #endregion

    }
}
AaronLS
fuente
Arreglé un pequeño detalle en tu código. Parece que dejó la thread1_parte de su cableado del AsyncCompletedEventHandler . Si mi edición fue un error, por favor ayúdenme a comprender lo que estaba sucediendo allí.
jp2code
1
@ jp2code No puede hacerlo thread1_Thread1Completed +=porque thread1_Thread1Completedes el nombre de una función, por lo que no puede ponerlo en el lado izquierdo de un operador de asignación. El lado izquierdo Thread1Completed +=se usa porque es un evento, por lo que puede aparecer en el lado izquierdo del operador de asignación para agregar controladores de eventos. Verpublic event AsyncCompletedEventHandler Thread1Completed;
AaronLS
Ya lo veo. No sé por qué no pude ver ese controlador de eventos en su #regionsección antes. Miré. ¡Honesto! :)
jp2code
2

Los hilos realmente no tienen valores de retorno. Sin embargo, si crea un delegado, puede invocarlo de forma asincrónica a través del BeginInvokemétodo. Esto ejecutará el método en un subproceso de grupo de subprocesos. Puede obtener cualquier valor de retorno, como llamar a través de EndInvoke.

Ejemplo:

static int GetAnswer() {
   return 42;
}

...

Func<int> method = GetAnswer;
var res = method.BeginInvoke(null, null); // provide args as needed
var answer = method.EndInvoke(res);

GetAnswerse ejecutará en un subproceso de grupo de subprocesos y cuando se complete, puede recuperar la respuesta a través de EndInvokecomo se muestra.

Brian Rasmussen
fuente
2

El BackgroundWorker es agradable cuando se desarrollan para Windows Forms.

Supongamos que desea aprobar una clase simple de un lado a otro:

class Anything {
    // Number and Text are for instructional purposes only
    public int Number { get; set; }
    public string Text { get; set; }
    // Data can be any object - even another class
    public object Data { get; set; }
}

Escribí una clase corta que hace lo siguiente:

  • Crear o borrar una lista
  • Iniciar un bucle
  • En bucle, cree un nuevo elemento para la lista
  • En bucle, crea un hilo
  • En bucle, envíe el elemento como parámetro al hilo
  • En bucle, comienza el hilo
  • En bucle, agregue hilo a la lista para ver
  • Después del bucle, une cada hilo
  • Una vez que se hayan completado todas las uniones, muestre los resultados

Desde dentro de la rutina del hilo:

  • Bloqueo de llamadas para que solo 1 hilo pueda ingresar a esta rutina a la vez (otros tienen que esperar)
  • Publica información sobre el artículo.
  • Modifica el artículo.
  • Cuando se completa el hilo, los datos se muestran en la consola.

Agregar un delegado puede ser útil para publicar sus datos directamente en su hilo principal, pero es posible que deba usar Invoke si algunos de los elementos de datos no son seguros para el hilo.

class AnyTask {

    private object m_lock;

    public AnyTask() {
        m_lock = new object();
    }
    // Something to use the delegate
    public event MainDelegate OnUpdate;

    public void Test_Function(int count) {
        var list = new List<Thread>(count);
        for (var i = 0; i < count; i++) {
            var thread = new Thread(new ParameterizedThreadStart(Thread_Task));
            var item = new Anything() {
                Number = i,
                Text = String.Format("Test_Function #{0}", i)
            };
            thread.Start(item);
            list.Add(thread);
        }
        foreach (var thread in list) {
            thread.Join();
        }
    }

    private void MainUpdate(Anything item, bool original) {
        if (OnUpdate != null) {
            OnUpdate(item, original);
        }
    }

    private void Thread_Task(object parameter) {
        lock (m_lock) {
            var item = (Anything)parameter;
            MainUpdate(item, true);
            item.Text = String.Format("{0}; Thread_Task #{1}", item.Text, item.Number);
            item.Number = 0;
            MainUpdate(item, false);
        }
    }

}

Para probar esto, cree una pequeña aplicación de consola y colóquela en el archivo Program.cs :

// A delegate makes life simpler
delegate void MainDelegate(Anything sender, bool original);

class Program {

    private const int COUNT = 15;
    private static List<Anything> m_list;

    static void Main(string[] args) {
        m_list = new List<Anything>(COUNT);
        var obj = new AnyTask();
        obj.OnUpdate += new MainDelegate(ThreadMessages);
        obj.Test_Function(COUNT);
        Console.WriteLine();
        foreach (var item in m_list) {
            Console.WriteLine("[Complete]:" + item.Text);
        }
        Console.WriteLine("Press any key to exit.");
        Console.ReadKey();
    }

    private static void ThreadMessages(Anything item, bool original) {
        if (original) {
            Console.WriteLine("[main method]:" + item.Text);
        } else {
            m_list.Add(item);
        }
    }

}

Aquí hay una captura de pantalla de lo que obtuve con esto:

Salida de consola

Espero que otros puedan entender lo que he intentado explicar.

Disfruto trabajando en hilos y usando delegados. Hacen que C # sea muy divertido.

Apéndice: para codificadores VB

Quería ver qué implicaba escribir el código anterior como una aplicación de consola VB. La conversión involucró algunas cosas que no esperaba, así que actualizaré este hilo aquí para aquellos que quieran saber cómo hilo en VB.

Imports System.Threading

Delegate Sub MainDelegate(sender As Anything, original As Boolean)

Class Main

    Private Const COUNT As Integer = 15
    Private Shared m_list As List(Of Anything)

    Public Shared Sub Main(args As String())
        m_list = New List(Of Anything)(COUNT)
        Dim obj As New AnyTask()
        AddHandler obj.OnUpdate, New MainDelegate(AddressOf ThreadMessages)
        obj.Test_Function(COUNT)
        Console.WriteLine()
        For Each item As Anything In m_list
            Console.WriteLine("[Complete]:" + item.Text)
        Next
        Console.WriteLine("Press any key to exit.")
        Console.ReadKey()
    End Sub

    Private Shared Sub ThreadMessages(item As Anything, original As Boolean)
        If original Then
            Console.WriteLine("[main method]:" + item.Text)
        Else
            m_list.Add(item)
        End If
    End Sub

End Class

Class AnyTask

    Private m_lock As Object

    Public Sub New()
        m_lock = New Object()
    End Sub
    ' Something to use the delegate
    Public Event OnUpdate As MainDelegate

    Public Sub Test_Function(count As Integer)
        Dim list As New List(Of Thread)(count)
        For i As Int32 = 0 To count - 1
            Dim thread As New Thread(New ParameterizedThreadStart(AddressOf Thread_Task))
            Dim item As New Anything()
            item.Number = i
            item.Text = String.Format("Test_Function #{0}", i)
            thread.Start(item)
            list.Add(thread)
        Next
        For Each thread As Thread In list
            thread.Join()
        Next
    End Sub

    Private Sub MainUpdate(item As Anything, original As Boolean)
        RaiseEvent OnUpdate(item, original)
    End Sub

    Private Sub Thread_Task(parameter As Object)
        SyncLock m_lock
            Dim item As Anything = DirectCast(parameter, Anything)
            MainUpdate(item, True)
            item.Text = [String].Format("{0}; Thread_Task #{1}", item.Text, item.Number)
            item.Number = 0
            MainUpdate(item, False)
        End SyncLock
    End Sub

End Class


Class Anything
    ' Number and Text are for instructional purposes only
    Public Property Number() As Integer
        Get
            Return m_Number
        End Get
        Set(value As Integer)
            m_Number = value
        End Set
    End Property
    Private m_Number As Integer
    Public Property Text() As String
        Get
            Return m_Text
        End Get
        Set(value As String)
            m_Text = value
        End Set
    End Property
    Private m_Text As String
    ' Data can be anything or another class
    Public Property Data() As Object
        Get
            Return m_Data
        End Get
        Set(value As Object)
            m_Data = value
        End Set
    End Property
    Private m_Data As Object
End Class
jp2code
fuente
1
class Program
{
    static void Main(string[] args)
    {
        string returnValue = null;
       new Thread(
          () =>
          {
              returnValue =test() ; 
          }).Start();
        Console.WriteLine(returnValue);
        Console.ReadKey();
    }

    public static string test()
    {
        return "Returning From Thread called method";
    }
}
Shyam Sundar Shah
fuente
El ejemplo proporcionado es incorrecto, tienes suerte de que te haya funcionado. Imagina la siguiente situación test(){ Thread.Sleep(5000); /*Highly time demanding process*/ return "Returned from test()";}. En este caso, el hilo independiente no tendría tiempo para asignar un nuevo valor a la returnValuevariable. Como último recurso, puede guardar una referencia de hilo var standaloneThread = new Thread(()=> //...);y después de eso, iniciarlo de forma sincronizada standaloneThread.Start(); standaloneThread.Join();. Pero esta ciertamente no es la mejor práctica.
AlexMelw
1

Una solución simple es pasar un parámetro por referencia a la función que se está ejecutando en el hilo y cambiar su valor en el hilo.

       // create a list of threads
        List<Thread> threads = new List<Thread>();


        //declare the ref params
        bool is1 = false;
        bool is2 = false;

        threads.Add(new Thread(() => myFunction(someVar, ref is1)));
        threads.Add(new Thread(() => myFunction(someVar, ref is2)));

        threads.ForEach(x => x.Start());

        // wait for threads to finish
        threads.ForEach(x => x.Join());

        //check the ref params
        if (!is1)
        {
          //do something
        }

        if (!is2)
        {
           //do somethign else
        }

Si no puede cambiar la función que se está ejecutando en la banda de rodadura, puede envolverla en otra función:

 bool theirFunction(var someVar){
   return false;
}


 void myFunction(var someVar ref bool result){
  result = theirFunction(myVar);
 }
CodeToad
fuente
por favor explique el voto negativo. Utilizo este patrón en mi propio código y funciona perfectamente bien.
CodeToad
0

Puede usar este código:

 private Object MyThread(Object Data)
      {
        Object response = null;
        Thread newThread = new Thread(() =>
        {
            response = MyFunction(Data);
            //MyFunction Is Function that you Define
        });
        newThread.Start();
        newThread.Join();
        return response;
      }
Ali asghar Fendereski
fuente
-1

No soy un experto en subprocesos, por eso lo hice así:

Creé un archivo de configuración y

Dentro del nuevo hilo:

Setting.Default.ValueToBeSaved;
Setting.Default.Save();

Luego recojo ese valor cada vez que lo necesito.

Patrik
fuente