Retornando um valor do tópico?

102

Como faço para retornar um valor de um thread?

Sam
fonte

Respostas:

94

Uma das maneiras mais fáceis de obter um valor de retorno de um encadeamento é usar fechamentos. Crie uma variável que conterá o valor de retorno do thread e, em seguida, capture-o em uma expressão lambda. Atribua o valor de "retorno" a esta variável do thread de trabalho e, depois que o thread terminar, você poderá usá-lo do thread pai.

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
fonte
3
Não lock(value) { value = "Hello world"; }seria melhor lidar com a gravação de vários valores de thread?
checksum
4
@checksum: Neste caso específico, é desnecessário porque nenhuma leitura ou gravação valueestá ocorrendo ao mesmo tempo. Mas, sim, sempre esteja atento quando um bloqueio é necessário.
Brian Gideon
Idéia fantástica! Funciona de forma brilhante e deve ser a resposta aceita.
MerseyViking de
34

Depende de como você deseja criar o thread e a versão .NET disponível:

.NET 2.0+:

A) Você pode criar o Threadobjeto diretamente. Neste caso, você pode usar "closure" - declarar a variável e capturá-la usando a expressão lambda:

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

B) Você pode usar delegados e IAsyncResultvalor de retorno do 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) Você pode usar a BackgroundWorkerclasse. Neste caso, você pode usar a variável capturada (como com o Threadobjeto) ou manipular o 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 do .NET 4.0, você pode usar a biblioteca paralela de tarefas e a Taskclasse para iniciar seus threads. A classe genérica Task<TResult>permite que você obtenha o valor de retorno da Resultpropriedade:

//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 do .NET 4.5, você também pode usar async/ awaitkeywords para retornar o valor da tarefa diretamente em vez de obter a Resultpropriedade:

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

Nota: o método, que contém o código acima, deve ser marcado com a asyncpalavra - chave.

Por muitas razões, o uso da Biblioteca Paralela de Tarefas é a maneira preferível de trabalhar com threads.

Igor Bendrup
fonte
33

Eu usaria a abordagem BackgroundWorker e retornaria o resultado em e.Result.

EDITAR:

Isso é comumente associado a WinForms e WPF, mas pode ser usado por qualquer tipo de aplicativo .NET. Este é um exemplo de código para um aplicativo de console 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);
            }
        }
    }
}

Resultado:

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

ATUALIZAÇÃO DE 2014

Veja a resposta de @ Roger abaixo.

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

Ele ressalta que você pode usar uma Tarefa que retorna um Task<T>e verifique Task<T>.Result.

Eric J.
fonte
Sim, mas só se aplica a WinForms e WPF.
Henk Holterman
@Henk: Não é verdade. Acabei de escrever um aplicativo de console simples que usa BackgroundWorker apenas para ter certeza :-) Editei minha postagem com aquele código.
Eric J.
Eric, coloque algumas writelines em seu código para ver quando o que acontece e em que ThreadId. Pode não ocorrer como você espera. (Concluído será executado antes do Dowork terminar, e não no tópico principal). O Bgw precisa de um MessagePump.
Henk Holterman
@Henk: Você está meio certo. Concluído é executado no mesmo encadeamento que o BackgroundWorker, mas é executado após a conclusão do DoWork. Veja a saída na resposta editada.
Eric J.
2
Não há condição de corrida, porque exatamente um encadeamento define a variável e exatamente um encadeamento a lê, e a ordem exata de conjunto versus leitura não importa para a execução correta do código (ou seja, a condição de encerramento pode ocorrer no encadeamento principal um pouco mais cedo ou mais tarde, dependendo da ordem em que os threads estão agendados, mas de qualquer forma, você ainda obterá um resultado correto).
Eric J.
21

Um thread não é um método - você normalmente não "retorna" um valor.

No entanto, se estiver tentando recuperar um valor dos resultados de algum processamento, você terá muitas opções, sendo as duas principais:

  • Você pode sincronizar uma parte compartilhada de dados e configurá-la apropriadamente.
  • Você também pode passar os dados de volta em alguma forma de retorno de chamada.

Realmente depende de como você está criando o thread e como deseja usá-lo, bem como da linguagem / estrutura / ferramentas que está usando.

Reed Copsey
fonte
15

Minha classe favorita executa qualquer método em outro thread com apenas 2 linhas 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);
    }

Esteja ciente de que longFunctionComplete NÃO será executado no mesmo encadeamento que starthework.

Para métodos que usam parâmetros, você sempre pode usar fechamentos ou expandir a classe.

Erik
fonte
3
Não está claro para todos ... stuffReturned ?, resultOfWork, PrintWorkComplete? etc.
Lost_In_Library de
14

Aqui está um exemplo simples usando um 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();
  }
}

Há uma série fantástica sobre threading em Threading em C # .

JP Alioto
fonte
9

Basta usar a abordagem de delegado.

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

Agora faça a função Multiply que funcionará em outro thread:

int Multiply(int x, int y)
{
    return x * y;
}
hospedagem de iogues
fonte
3
Por que a resposta abaixo é "salve em um arquivo de texto e recupere-o"?
Jon
7

Eu me deparei com este segmento ao tentar obter o valor de retorno de um método que é executado dentro de um segmento. Pensei em postar minha solução que funcionasse.

Esta solução usa uma classe para armazenar o método a ser executado (indiretamente) e armazena o valor de retorno. A classe pode ser usada para qualquer função e qualquer tipo de retorno. Você apenas instancia o objeto usando o tipo de valor de retorno e, em seguida, passa a função para chamar por meio de um lambda (ou delegado).


Implementação 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, você pode usar um Lambda (ou um delegado). Aqui está o exemplo 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...
}

Implementação VB.NET 2008


Qualquer pessoa usando o VB.NET 2008 não pode usar lambdas com métodos de retorno sem valor. Isso afeta a ThreadedMethodclasse, então faremos ExecuteMethodretornar o valor da função. Isso não machuca 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
Matt
fonte
7

Com o .NET Framework mais recente, é possível retornar um valor de um thread separado usando uma Task, onde a propriedade Result bloqueia o thread de chamada até que a tarefa seja concluída:

  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 obter detalhes, consulte http://msdn.microsoft.com/en-us/library/dd537613(v=vs.110).aspx

user8128167
fonte
5

Delegados ThreadStart em C # usados ​​para iniciar threads têm o tipo de retorno 'void'.

Se você deseja obter um 'valor de retorno' de uma thread, você deve escrever em um local compartilhado (de uma maneira segura para thread) e ler a partir daí quando a thread tiver concluído a execução.

jscharf
fonte
5

Se você não quiser usar um BackgroundWorker e apenas usar um Thread normal, pode disparar um evento para retornar dados 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
fonte
Corrigi um pequeno detalhe em seu código. Parece que você deixou de fora a thread1_parte da fiação do AsyncCompletedEventHandler . Se minha edição estava errada, por favor me ajude a entender o que estava acontecendo lá.
jp2code
1
@ jp2code Você não pode fazer thread1_Thread1Completed +=porque thread1_Thread1Completedé o nome de uma função, portanto, você não pode colocá-lo no lado esquerdo de um operador de atribuição. O lado esquerdo Thread1Completed +=é usado porque é um evento, portanto, pode aparecer no lado esquerdo do operador de atribuição para adicionar manipuladores de eventos. Verpublic event AsyncCompletedEventHandler Thread1Completed;
AaronLS
Eu vejo agora. Não sei por que não pude ver esse manipulador de eventos em sua #regionseção antes. Eu olhei. Honesto! :)
jp2code
2

Threads realmente não têm valores de retorno. No entanto, se você criar um delegado, poderá invocá-lo de forma assíncrona por meio do BeginInvokemétodo. Isso executará o método em um thread do pool de threads. Você pode obter qualquer valor de retorno de como chamada via EndInvoke.

Exemplo:

static int GetAnswer() {
   return 42;
}

...

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

GetAnswerserá executado em um thread do pool de threads e, quando concluído, você poderá recuperar a resposta EndInvokecomo mostrado.

Brian Rasmussen
fonte
2

O BackgroundWorker é bom quando se desenvolve para Windows Forms.

Digamos que você queira passar em uma aula simples de um lado para outro:

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

Eu escrevi uma curta aula que faz o seguinte:

  • Criar ou limpar uma lista
  • Comece um loop
  • Em loop, crie um novo item para a lista
  • Em loop, crie um tópico
  • Em loop, envie o item como um parâmetro para o thread
  • Em loop, inicie o tópico
  • Em loop, adicione o tópico à lista para assistir
  • Após o loop, junte cada thread
  • Depois que todas as junções forem concluídas, exiba os resultados

De dentro da rotina de discussão:

  • Chame o bloqueio para que apenas 1 thread possa entrar nesta rotina de cada vez (os outros têm que esperar)
  • Publique informações sobre o item.
  • Modifique o item.
  • Quando o encadeamento é concluído, os dados são exibidos no console.

Adicionar um delegado pode ser útil para postar seus dados diretamente de volta ao thread principal, mas você pode precisar usar Invoke se alguns dos itens de dados não forem seguros para thread.

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 testar isso, crie um pequeno aplicativo de console e coloque-o no arquivo 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);
        }
    }

}

Aqui está uma captura de tela do que obtive com isso:

Saída do console

Espero que outras pessoas possam entender o que tentei explicar.

Gosto de trabalhar em tópicos e usar delegados. Eles tornam o C # muito divertido.

Apêndice: Para codificadores VB

Eu queria ver o que estava envolvido na escrita do código acima como um aplicativo de console VB. A conversão envolveu algumas coisas que eu não esperava, então irei atualizar este tópico aqui para aqueles que desejam saber como encadear em 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
fonte
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
fonte
O exemplo fornecido está errado, você teve sorte de ter funcionado para você. Imagine a seguinte situação test(){ Thread.Sleep(5000); /*Highly time demanding process*/ return "Returned from test()";}. Nesse caso, o encadeamento autônomo não teria tempo para atribuir um novo valor à returnValuevariável. Como último recurso, você pode salvar uma referência de thread var standaloneThread = new Thread(()=> //...);e, depois disso, iniciá-la de forma sincronizada standaloneThread.Start(); standaloneThread.Join();. Mas essa certamente não é a melhor prática.
AlexMelw
1

Uma solução simples é passar um parâmetro por ref para a função que está sendo executada no thread e alterar seu valor no thread.

       // 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
        }

Se você não pode alterar a função que está sendo executada na banda de rodagem, pode envolvê-la em outra função:

 bool theirFunction(var someVar){
   return false;
}


 void myFunction(var someVar ref bool result){
  result = theirFunction(myVar);
 }
CodeToad
fonte
por favor, explique o downvote. Eu uso esse padrão em meu próprio código e funciona perfeitamente bem.
CodeToad
0

Pode 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
fonte
-1

Não sou especialista em threading, é por isso que fiz assim:

Eu criei um arquivo de configurações e

Dentro do novo tópico:

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

Então, pego esse valor sempre que preciso.

Patrik
fonte