Returning a value from thread?

前端 未结 17 2201
不思量自难忘°
不思量自难忘° 2020-11-27 09:45

How do I return a value from a thread?

相关标签:
17条回答
  • 2020-11-27 10:26

    Simply use the delegate approach.

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

    Now make Multiply function that will work on another thread:

    int Multiply(int x, int y)
    {
        return x * y;
    }
    
    0 讨论(0)
  • 2020-11-27 10:29

    I came across this thread when also trying to obtain the return value of a method that gets executed within a Thread. I thought I would post my solution that works.

    This solution uses an class to store both the method to be executed (indirectly) and stores the returning value. The class can be used for any function and any return type. You just instantiate the object using the return value type and then pass the function to call via a lambda (or delegate).


    C# 3.0 Implementation


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

    To use this code you can use a Lambda (or a delegate). Here is the example using 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...
    }
    

    VB.NET 2008 Implementation


    Anyone using VB.NET 2008 can't use lambdas with non-value returning methods. This affects the ThreadedMethod class, so we'll make ExecuteMethod return the value of the function. This doesn't hurt anything.

    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
    
    0 讨论(0)
  • 2020-11-27 10:30

    A thread isn't a method - you don't normally "return" a value.

    However, if you're trying to fetch a value back from the results of some processing, you have many options, the two main ones being:

    • You can synchronize a shared piece of data, and set it appropriately.
    • You can also pass the data back in some form of callback.

    It really depends on how you're creating the thread, and how you want to use it, as well as the language/framework/tools you're using.

    0 讨论(0)
  • 2020-11-27 10:32

    Threads do not really have return values. However, if you create a delegate, you can invoke it asynchronously via the BeginInvoke method. This will execute the method on a thread pool thread. You can get any return value from such as call via EndInvoke.

    Example:

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

    GetAnswer will execute on a thread pool thread and when completed you can retrieve the answer via EndInvoke as shown.

    0 讨论(0)
  • 2020-11-27 10:32

    The BackgroundWorker is nice when developing for Windows Forms.

    Say you wanted to pass a simple class back and forth:

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

    I wrote up a short class that does the following:

    • Create or Clear a list
    • Start a loop
    • In loop, create a new item for the list
    • In loop, create a thread
    • In loop, send the item as a parameter to the thread
    • In loop, start the thread
    • In loop, add thread to list to watch
    • After loop, join each thread
    • After all joins have completed, display the results

    From inside the thread routine:

    • Call lock so that only 1 thread can enter this routine at a time (others have to wait)
    • Post information about the item.
    • Modify the item.
    • When the thread completes, the data is displayed on the console.

    Adding a delegate can be useful for posting your data directly back to your main thread, but you may need to use Invoke if some of the data items are not thread safe.

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

    To test this, create a little Console Application, and put this in the Program.cs file:

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

    Here is a screenshot of what I got with this:

    Console Output

    I hope others can understand what I've tried to explain.

    I enjoy working on threads and using delegates. They make C# a lot of fun.

    Appendix: For VB Coders

    I wanted to see what was involved in writing the code above as a VB Console Application. The conversion involved a few things I didn't expect, so I will update this thread here for those wanting to know how to thread in 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
    
    0 讨论(0)
  • 2020-11-27 10:38

    One of the easiest ways to get a return value from a thread is to use closures. Create a variable that will hold the return value from the thread and then capture it in a lambda expression. Assign the "return" value to this variable from the worker thread and then once that thread ends you can use it from the parent thread.

    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
    }
    
    0 讨论(0)
提交回复
热议问题