Einen Wert vom Thread zurückgeben?

102

Wie gebe ich einen Wert von einem Thread zurück?

Sam
quelle

Antworten:

93

Eine der einfachsten Möglichkeiten, einen Rückgabewert von einem Thread abzurufen, ist die Verwendung von Verschlüssen. Erstellen Sie eine Variable, die den Rückgabewert des Threads enthält, und erfassen Sie ihn dann in einem Lambda-Ausdruck. Weisen Sie dieser Variablen aus dem Arbeitsthread den Wert "return" zu. Sobald dieser Thread endet, können Sie ihn aus dem übergeordneten Thread verwenden.

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
quelle
3
Wäre es nicht lock(value) { value = "Hello world"; }besser, mit dem Schreiben mehrerer Thread-Werte umzugehen?
Prüfsumme
4
@checksum: In diesem speziellen Fall ist dies nicht erforderlich, da keine Lese- oder Schreibvorgänge valuegleichzeitig ausgeführt werden. Aber denken Sie immer daran, wann ein Schloss notwendig ist.
Brian Gideon
Fantastische Idee! Funktioniert hervorragend und sollte die akzeptierte Antwort sein.
MerseyViking
33

Ich würde den BackgroundWorker- Ansatz verwenden und das Ergebnis in e.Result zurückgeben.

BEARBEITEN:

Dies ist normalerweise mit WinForms und WPF verbunden, kann jedoch von jeder Art von .NET-Anwendung verwendet werden. Hier ist Beispielcode für eine Konsolen-App, die BackgroundWorker verwendet:

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

Ausgabe:

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

UPDATE 2014

Siehe @ Rogers Antwort unten.

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

Er weist darauf hin, dass Sie eine Aufgabe verwenden können, die a zurückgibt Task<T>, und überprüfen Task<T>.Result.

Eric J.
quelle
Ja, aber es gilt nur für WinForms und WPF.
Henk Holterman
@Henk: Nicht wahr. Ich habe gerade eine einfache Konsolen-App geschrieben, die BackgroundWorker verwendet, um sicherzugehen :-) Ich habe meinen Beitrag mit diesem Code bearbeitet.
Eric J.
Eric, schreibe ein paar Zeilen in deinen Code, um zu sehen, wann was passiert und auf welcher ThreadId. Es könnte nicht so laufen, wie Sie es erwarten. (Abgeschlossen wird ausgeführt, bevor Dowork fertig ist, und nicht im Haupt-Thread). Das Bgw benötigt eine MessagePump.
Henk Holterman
@Henk: Du hast halb recht. Abgeschlossen wird im selben Thread wie der BackgroundWorker ausgeführt, jedoch nach Abschluss von DoWork. Siehe Ausgabe in bearbeiteter Antwort.
Eric J.
2
Es gibt keine Race-Bedingung, da genau ein Thread die Variable setzt und genau ein Thread sie liest und die genaue Reihenfolge von Set vs. Read für die korrekte Ausführung des Codes keine Rolle spielt (dh die Beendigungsbedingung kann im Haupt-Thread auftreten etwas früher oder später, abhängig von der Reihenfolge, in der die Threads geplant sind, aber in beiden Fällen erhalten Sie immer noch ein korrektes Ergebnis.
Eric J.
33

Dies hängt davon ab, wie Sie den Thread und die verfügbare .NET-Version erstellen möchten:

.NET 2.0+:

A) Sie können das ThreadObjekt direkt erstellen . In diesem Fall könnten Sie "Schließung" verwenden - deklarieren Sie die Variable und erfassen Sie sie mit dem Lambda-Ausdruck:

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

B) Sie können Delegaten und IAsyncResultund Rückgabewert von EndInvoke()Methode verwenden:

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

C) Sie können BackgroundWorkerKlasse verwenden. In diesem Fall können Sie eine erfasste Variable (wie bei einem ThreadObjekt) oder ein Handle- RunWorkerCompletedEreignis verwenden:

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

Ab .NET 4.0 können Sie die Task Parallel Library und die TaskKlasse verwenden, um Ihre Threads zu starten. Mit der generischen Klasse Task<TResult>können Sie den Rückgabewert von der ResultEigenschaft abrufen:

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

Ab .NET 4.5 können Sie auch async/ awaitkeywords verwenden, um den Wert direkt von der Aufgabe zurückzugeben, anstatt die ResultEigenschaft zu erhalten:

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

Hinweis: Die Methode, die den obigen Code enthält, sollte mit markiert werden async Schlüsselwort gekennzeichnet werden.

Aus vielen Gründen ist die Verwendung der Task Parallel Library die bevorzugte Methode zum Arbeiten mit Threads.

Igor Bendrup
quelle
21

Ein Thread ist keine Methode - normalerweise "geben" Sie keinen Wert zurück.

Wenn Sie jedoch versuchen, einen Wert aus den Ergebnissen einer Verarbeitung zurückzugewinnen, stehen Ihnen viele Optionen zur Verfügung. Die beiden wichtigsten sind:

  • Sie können ein freigegebenes Datenelement synchronisieren und entsprechend festlegen.
  • Sie können die Daten auch in Form eines Rückrufs zurückgeben.

Es hängt wirklich davon ab, wie Sie den Thread erstellen und wie Sie ihn verwenden möchten sowie von der Sprache / dem Framework / den Tools, die Sie verwenden.

Reed Copsey
quelle
15

Meine Lieblingsklasse führt eine beliebige Methode in einem anderen Thread mit nur 2 Codezeilen aus.

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

Verwendung

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

Beachten Sie, dass longFunctionComplete NICHT im selben Thread wie starthework ausgeführt wird.

Für Methoden, die Parameter annehmen, können Sie jederzeit Closures verwenden oder die Klasse erweitern.

Erik
quelle
3
Nicht für alle klar ... stuffReturned?, ResultOfWork, PrintWorkComplete? usw.
Lost_In_Library
14

Hier ist ein einfaches Beispiel mit einem Delegaten ...

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

Es gibt eine großartige Serie zum Thema Threading bei Threading in C # .

JP Alioto
quelle
9

Verwenden Sie einfach den Delegatenansatz.

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

Stellen Sie nun die Multiplikationsfunktion ein, die für einen anderen Thread funktioniert:

int Multiply(int x, int y)
{
    return x * y;
}
Yogihosting
quelle
3
Warum lautet diese Antwort unten "Speichern Sie sie in einer Textdatei und rufen Sie sie ab"?
Jon
7

Ich bin auf diesen Thread gestoßen, als ich auch versucht habe, den Rückgabewert einer Methode zu erhalten, die in einem Thread ausgeführt wird. Ich dachte, ich würde meine Lösung veröffentlichen, die funktioniert.

Diese Lösung verwendet eine Klasse, um sowohl die auszuführende Methode (indirekt) als auch den Rückgabewert zu speichern. Die Klasse kann für jede Funktion und jeden Rückgabetyp verwendet werden. Sie instanziieren das Objekt einfach mit dem Rückgabewerttyp und übergeben dann die aufzurufende Funktion über ein Lambda (oder einen Delegaten).


C # 3.0-Implementierung


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

Um diesen Code zu verwenden, können Sie einen Lambda (oder einen Delegaten) verwenden. Hier ist das Beispiel mit 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...
}

Implementierung von VB.NET 2008


Jeder, der VB.NET 2008 verwendet, kann keine Lambdas mit nicht wertvollen Rückgabemethoden verwenden. Dies wirkt sich auf die ThreadedMethodKlasse aus, daher wird ExecuteMethodder Wert der Funktion zurückgegeben. Das tut nichts weh.

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
quelle
7

Mit dem neuesten .NET Framework ist es möglich, einen Wert aus einem separaten Thread mithilfe einer Aufgabe zurückzugeben, wobei die Result-Eigenschaft den aufrufenden Thread blockiert, bis die Aufgabe abgeschlossen ist:

  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;

Weitere Informationen finden Sie unter http://msdn.microsoft.com/en-us/library/dd537613(v=vs.110).aspx

user8128167
quelle
5

ThreadStart-Delegaten in C #, die zum Starten von Threads verwendet werden, haben den Rückgabetyp 'void'.

Wenn Sie einen 'Rückgabewert' von einem Thread erhalten möchten, sollten Sie an einen freigegebenen Speicherort schreiben (in geeigneter threadsicherer Weise) und daraus lesen, wenn der Thread die Ausführung abgeschlossen hat.

jscharf
quelle
5

Wenn Sie keinen BackgroundWorker verwenden möchten und nur einen regulären Thread verwenden möchten, können Sie ein Ereignis auslösen, um Daten wie folgt zurückzugeben:

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
quelle
Ich habe ein kleines Detail in Ihrem Code behoben. Es sieht so aus, als hätten Sie den thread1_Teil seiner Verkabelung des AsyncCompletedEventHandler weggelassen . Wenn meine Bearbeitung fehlerhaft war, helfen Sie mir bitte zu verstehen, was dort vor sich ging.
JP2-Code
1
@ jp2code Sie können dies nicht tun, thread1_Thread1Completed +=da thread1_Thread1Completedes sich um den Namen einer Funktion handelt. Sie können ihn also nicht auf der linken Seite eines Zuweisungsoperators platzieren. Die linke Seite Thread1Completed +=wird verwendet, da dies ein Ereignis ist. Daher kann es auf der linken Seite des Zuweisungsoperators angezeigt werden, um Ereignishandler hinzuzufügen. Siehepublic event AsyncCompletedEventHandler Thread1Completed;
AaronLS
Ich sehe es jetzt. Ich weiß nicht, warum ich diesen Ereignishandler in Ihrer #regionSektion vorher nicht sehen konnte . Ich schaute. Ehrlich! :)
jp2code
2

Threads haben nicht wirklich Rückgabewerte. Wenn Sie jedoch einen Delegaten erstellen, können Sie ihn über die BeginInvokeMethode asynchron aufrufen . Dadurch wird die Methode in einem Thread-Pool-Thread ausgeführt. Sie können einen beliebigen Rückgabewert erhalten, z. B. einen Anruf überEndInvoke .

Beispiel:

static int GetAnswer() {
   return 42;
}

...

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

GetAnswerwird in einem Thread-Pool-Thread ausgeführt und wenn Sie fertig sind, können Sie die Antwort über EndInvokewie gezeigt abrufen .

Brian Rasmussen
quelle
2

Der BackgroundWorker ist hilfreich bei der Entwicklung für Windows Forms.

Angenommen, Sie wollten eine einfache Klasse hin und her bestehen:

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

Ich habe eine kurze Klasse geschrieben, die Folgendes macht:

  • Erstellen oder Löschen einer Liste
  • Starten Sie eine Schleife
  • Erstellen Sie in einer Schleife ein neues Element für die Liste
  • Erstellen Sie in einer Schleife einen Thread
  • Senden Sie das Element in einer Schleife als Parameter an den Thread
  • Starten Sie in einer Schleife den Thread
  • Fügen Sie in einer Schleife einen Thread zur Liste hinzu, um ihn anzusehen
  • Verbinden Sie nach der Schleife jeden Thread
  • Zeigen Sie die Ergebnisse an, nachdem alle Verknüpfungen abgeschlossen wurden

Aus der Thread-Routine heraus:

  • Rufen Sie die Sperre auf, damit jeweils nur 1 Thread in diese Routine eintreten kann (andere müssen warten).
  • Veröffentlichen Sie Informationen zum Artikel.
  • Ändern Sie den Artikel.
  • Wenn der Thread abgeschlossen ist, werden die Daten auf der Konsole angezeigt.

Das Hinzufügen eines Delegaten kann nützlich sein, um Ihre Daten direkt in Ihren Hauptthread zurückzusenden. Möglicherweise müssen Sie jedoch Invoke verwenden, wenn einige der Datenelemente nicht threadsicher sind.

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

}

Um dies zu testen, erstellen Sie eine kleine Konsolenanwendung und fügen Sie diese in die Datei Program.cs ein :

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

}

Hier ist ein Screenshot von dem, was ich damit bekommen habe:

Konsolenausgabe

Ich hoffe, andere können verstehen, was ich zu erklären versucht habe.

Ich arbeite gerne an Threads und benutze Delegierte. Sie machen C # viel Spaß.

Anhang: Für VB-Codierer

Ich wollte sehen, worum es beim Schreiben des obigen Codes als VB-Konsolenanwendung geht. Die Konvertierung beinhaltete einige Dinge, die ich nicht erwartet hatte, daher werde ich diesen Thread hier für diejenigen aktualisieren, die wissen möchten, wie man VB einfädelt.

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
quelle
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
quelle
Das angegebene Beispiel ist falsch. Sie haben Glück, dass es für Sie funktioniert hat. Stellen Sie sich die folgende Situation vor test(){ Thread.Sleep(5000); /*Highly time demanding process*/ return "Returned from test()";}. In diesem Fall hätte der eigenständige Thread keine Zeit, der returnValueVariablen einen neuen Wert zuzuweisen . Als letzten Ausweg können Sie eine Thread-Referenz speichern var standaloneThread = new Thread(()=> //...);und anschließend synchron starten standaloneThread.Start(); standaloneThread.Join();. Dies ist jedoch sicherlich nicht die beste Vorgehensweise.
AlexMelw
1

Eine einfache Lösung besteht darin, einen Parameter per ref an die im Thread ausgeführte Funktion zu übergeben und seinen Wert im Thread zu ändern.

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

Wenn Sie die Funktion, die im Profil ausgeführt wird, nicht ändern können, können Sie eine andere Funktion einschließen:

 bool theirFunction(var someVar){
   return false;
}


 void myFunction(var someVar ref bool result){
  result = theirFunction(myVar);
 }
CodeToad
quelle
Bitte erläutern Sie die Abstimmung. Ich verwende dieses Muster in meinem eigenen Code und es funktioniert einwandfrei.
CodeToad
0

Kann diesen Code verwenden:

 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
quelle
-1

Ich bin kein Experte für Threading, deshalb habe ich es so gemacht:

Ich habe eine Einstellungsdatei erstellt und

Im neuen Thread:

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

Dann nehme ich diesen Wert auf, wann immer ich ihn brauche.

Patrik
quelle