Zwracasz wartość z wątku?


Odpowiedzi:


94

Jednym z najłatwiejszych sposobów uzyskania wartości zwracanej z wątku jest użycie domknięć. Utwórz zmienną, która będzie przechowywać wartość zwracaną z wątku, a następnie przechwyć ją w wyrażeniu lambda. Przypisz wartość „return” do tej zmiennej z wątku roboczego, a po zakończeniu tego wątku możesz użyć jej z wątku nadrzędnego.

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
}

3
Czy nie lock(value) { value = "Hello world"; }byłoby lepiej w obsłudze zapisu wartości wielu wątków?
suma kontrolna

4
@checksum: W tym konkretnym przypadku jest to niepotrzebne, ponieważ nie są odczytywane ani zapisywane valuew tym samym czasie. Ale tak, zawsze uważaj, kiedy zamek jest konieczny.
Brian Gideon

Fantastyczny pomysł! Działa genialnie i powinna być akceptowaną odpowiedzią.
MerseyViking

34

Zależy to od tego, jak chcesz utworzyć wątek i dostępnej wersji .NET:

.NET 2.0+:

A) Możesz stworzyć Threadobiekt bezpośrednio. W tym przypadku możesz użyć "zamknięcia" - zadeklaruj zmienną i przechwyć ją za pomocą wyrażenia lambda:

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

B) Możesz użyć delegatów IAsyncResulti zwrócić wartość z EndInvoke()metody:

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

C) Możesz użyć BackgroundWorkerclass. W tym przypadku możesz użyć przechwyconej zmiennej (jak w przypadku Threadobiektu) lub obsłużyć RunWorkerCompletedzdarzenie:

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

Począwszy od .NET 4.0, możesz użyć biblioteki równoległej zadań i Taskklasy, aby rozpocząć wątki. Klasa generyczna Task<TResult>pozwala na pobranie wartości zwracanej z Resultwłaściwości:

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

Począwszy od .NET 4.5 można również użyć async/ awaitwords do bezpośredniego zwracania wartości z zadania zamiast uzyskiwania Resultwłaściwości:

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

Uwaga: metodę zawierającą powyższy kod należy oznaczyć asyncsłowem kluczowym.

Z wielu powodów preferowanym sposobem pracy z wątkami jest użycie biblioteki równoległej zadań.


33

Chciałbym użyć BackgroundWorker podejście i zwraca wynik w e.Result.

EDYTOWAĆ:

Jest to często kojarzone z WinForms i WPF, ale może być używane przez dowolny typ aplikacji .NET. Oto przykładowy kod aplikacji konsoli korzystającej z 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);
            }
        }
    }
}

Wynik:

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

AKTUALIZACJA 2014

Zobacz odpowiedź @ Rogera poniżej.

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

Wskazuje, że możesz użyć zadania, które zwraca a Task<T>i sprawdzić Task<T>.Result.


Tak, ale dotyczy to tylko WinForms i WPF.
Henk Holterman

@Henk: Nieprawda. Właśnie napisałem prostą aplikację konsolową, która używa BackgroundWorker tylko po to, aby się upewnić :-) Zredagowałem mój post z tym kodem.
Eric J.

Eric, umieść kilka znaków zapisu w swoim kodzie, aby zobaczyć, co się stanie i na jakim ThreadId. Może nie przebiegać tak, jak się spodziewasz. (Zakończone będzie działać przed zakończeniem Dowork, a nie w głównym wątku). Bgw potrzebuje MessagePump.
Henk Holterman

@Henk: Masz rację w połowie. Completed działa w tym samym wątku co BackgroundWorker, ale działa po zakończeniu DoWork. Zobacz wyniki w edytowanej odpowiedzi.
Eric J.

2
Nie ma warunku wyścigu, ponieważ dokładnie jeden wątek ustawia zmienną i dokładnie jeden wątek ją odczytuje, a dokładna kolejność ustawiania względem odczytu nie ma znaczenia dla prawidłowego wykonania kodu (tzn. Warunek zakończenia może wystąpić w głównym wątku nieco wcześniej lub później, w zależności od kolejności, w jakiej są zaplanowane wątki, ale tak czy inaczej nadal otrzymujesz poprawny wynik).
Eric J.

21

Wątek nie jest metodą - normalnie nie „zwracasz” wartości.

Jeśli jednak próbujesz odzyskać wartość z wyników jakiegoś przetwarzania, masz wiele opcji, z których dwie główne to:

  • Możesz zsynchronizować udostępniony fragment danych i odpowiednio go ustawić.
  • Możesz również przekazać dane z powrotem w jakiejś formie oddzwonienia.

To naprawdę zależy od tego, jak tworzysz wątek i jak chcesz go używać, a także od języka / frameworka / narzędzi, których używasz.


15

Moja ulubiona klasa uruchamia dowolną metodę w innym wątku z zaledwie 2 wierszami kodu.

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

stosowanie

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

Uważaj, że longFunctionComplete NIE zostanie wykonany w tym samym wątku, co starthework.

W przypadku metod, które przyjmują parametry, zawsze możesz użyć domknięć lub rozwinąć klasę.


3
Nie dla wszystkich jasne ... rzeczyReturned ?, resultOfWork, PrintWorkComplete? itd.
Lost_In_Library,

14

Oto prosty przykład wykorzystujący delegata ...

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

W Threading w C # jest niesamowita seria dotycząca wątków .


9

Po prostu użyj podejścia delegata.

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

Teraz utwórz funkcję Multiply, która będzie działać w innym wątku:

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

3
Dlaczego poniższa odpowiedź brzmi „zapisz go w pliku tekstowym i pobierz”?
Jon

7

Natknąłem się na ten wątek, próbując również uzyskać wartość zwracaną metody, która jest wykonywana w ramach wątku. Pomyślałem, że opublikuję moje rozwiązanie, które działa.

To rozwiązanie wykorzystuje klasę do przechowywania zarówno metody do wykonania (pośrednio), jak i przechowuje zwracaną wartość. Klasa może być używana dla dowolnej funkcji i dowolnego typu zwracanego. Po prostu tworzysz instancję obiektu przy użyciu zwracanego typu wartości, a następnie przekazujesz funkcję do wywołania za pośrednictwem lambda (lub delegata).


Implementacja 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();
    }
}

Aby użyć tego kodu, możesz użyć Lambda (lub delegata). Oto przykład wykorzystujący lambdy:

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

Wdrożenie VB.NET 2008


Nikt, kto używa VB.NET 2008, nie może używać lambd z metodami zwracającymi niezwiązane z wartością. Ma to wpływ na ThreadedMethodklasę, więc zwrócimy ExecuteMethodwartość funkcji. To niczego nie boli.

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

7

W najnowszej wersji .NET Framework możliwe jest zwrócenie wartości z oddzielnego wątku za pomocą zadania, w którym właściwość Result blokuje wątek wywołujący do momentu zakończenia zadania:

  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;

Aby uzyskać szczegółowe informacje, zobacz http://msdn.microsoft.com/en-us/library/dd537613(v=vs.110).aspx


5

Delegaty ThreadStart w C # używane do uruchamiania wątków mają zwracany typ „void”.

Jeśli chcesz uzyskać `` wartość zwracaną '' z wątku, powinieneś napisać do udostępnionej lokalizacji (w odpowiedni sposób bezpieczny dla wątków) i odczytać z niej, gdy wątek zakończy wykonywanie.


5

Jeśli nie chcesz używać BackgroundWorkera i po prostu używasz zwykłego wątku, możesz uruchomić zdarzenie, aby zwrócić takie dane:

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

    }
}

Poprawiłem drobny szczegół w twoim kodzie. Wygląda na to, thread1_że pominąłeś część jego okablowania AsyncCompletedEventHandler . Jeśli moja zmiana była błędna, pomóż mi zrozumieć, co się tam dzieje.
jp2code

1
@ jp2code Nie możesz tego zrobić, thread1_Thread1Completed +=ponieważ thread1_Thread1Completedjest to nazwa funkcji, więc nie możesz jej umieścić po lewej stronie operatora przypisania. Lewa strona Thread1Completed +=jest używana, ponieważ jest to zdarzenie, więc może pojawić się po lewej stronie operatora przypisania, aby dodać programy obsługi zdarzeń. Zobaczpublic event AsyncCompletedEventHandler Thread1Completed;
AaronLS

Teraz to widzę. Nie wiem, dlaczego wcześniej nie widziałem tego programu obsługi zdarzeń w twojej #regionsekcji. Spojrzałem. Szczery! :)
jp2code

2

Wątki tak naprawdę nie zwracają wartości. Jeśli jednak utworzysz delegata, możesz wywołać go asynchronicznie za pomocą BeginInvokemetody. Spowoduje to wykonanie metody w wątku puli wątków. Możesz uzyskać dowolną wartość zwracaną z, na przykład call via EndInvoke.

Przykład:

static int GetAnswer() {
   return 42;
}

...

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

GetAnswerwykona w wątku puli wątków, a po zakończeniu możesz pobrać odpowiedź, EndInvokejak pokazano.


2

BackgroundWorker jest miła przy opracowywaniu dla Windows Forms.

Powiedzmy, że chcesz zaliczyć proste zajęcia w tę iz powrotem:

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

Napisałem krótkie zajęcia, które obejmują:

  • Utwórz lub wyczyść listę
  • Rozpocznij pętlę
  • W pętli utwórz nową pozycję na liście
  • W pętli utwórz wątek
  • W pętli wyślij element jako parametr do wątku
  • W pętli rozpocznij wątek
  • W pętli dodaj wątek do listy do obejrzenia
  • Po pętli dołącz do każdego wątku
  • Po zakończeniu wszystkich połączeń wyświetl wyniki

Od wewnątrz procedury wątku:

  • Zablokuj połączenie, aby tylko 1 wątek mógł wejść w tę procedurę naraz (inne muszą czekać)
  • Opublikuj informacje o przedmiocie.
  • Zmodyfikuj element.
  • Po zakończeniu wątku dane są wyświetlane na konsoli.

Dodanie delegata może być przydatne do wysyłania danych bezpośrednio z powrotem do głównego wątku, ale może być konieczne użycie Invoke, jeśli niektóre elementy danych nie są bezpieczne dla wątków.

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

}

Aby to przetestować, utwórz małą aplikację konsolową i umieść ją w pliku 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);
        }
    }

}

Oto zrzut ekranu tego, co mam z tym:

Wyjście konsoli

Mam nadzieję, że inni zrozumieją to, co próbowałem wyjaśnić.

Lubię pracować nad wątkami i korzystać z delegatów. Sprawiają, że C # jest świetną zabawą.

Dodatek: dla koderów VB

Chciałem zobaczyć, co wymagało napisania powyższego kodu jako aplikacji konsoli VB. Konwersja obejmowała kilka rzeczy, których się nie spodziewałem, więc zaktualizuję ten wątek tutaj dla tych, którzy chcą wiedzieć, jak wątkować w 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

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

Podany przykład jest błędny, masz szczęście, że zadziałał. Wyobraź sobie następującą sytuację test(){ Thread.Sleep(5000); /*Highly time demanding process*/ return "Returned from test()";}. W takim przypadku samodzielny wątek nie miałby czasu na przypisanie nowej wartości do returnValuezmiennej. W ostateczności możesz zapisać odwołanie do wątku, var standaloneThread = new Thread(()=> //...);a następnie uruchomić je w zsynchronizowany sposób standaloneThread.Start(); standaloneThread.Join();. Ale z pewnością nie jest to najlepsza praktyka.
AlexMelw

1

Prostym rozwiązaniem jest przekazanie parametru przez ref do funkcji działającej w wątku i zmiana jego wartości w wątku.

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

Jeśli nie możesz zmienić funkcji działającej w bieżniku, możesz zawinąć ją w inną funkcję:

 bool theirFunction(var someVar){
   return false;
}


 void myFunction(var someVar ref bool result){
  result = theirFunction(myVar);
 }

proszę wyjaśnić głos przeciw. Używam tego wzorca we własnym kodzie i działa on doskonale.
CodeToad

0

Może używać tego kodu:

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

-1

Nie jestem ekspertem w gwintowaniu, dlatego zrobiłem to tak:

Utworzyłem plik ustawień i

W nowym wątku:

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

Potem zbieram tę wartość, kiedy jej potrzebuję.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.