Trả lại một giá trị từ chuỗi?


Câu trả lời:


93

Một trong những cách dễ nhất để nhận giá trị trả về từ một luồng là sử dụng các bao đóng. Tạo một biến sẽ giữ giá trị trả về từ luồng và sau đó nắm bắt nó trong một biểu thức lambda. Gán giá trị "return" cho biến này từ luồng worker và sau đó khi luồng đó kết thúc, bạn có thể sử dụng nó từ luồng cha.

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
Sẽ không lock(value) { value = "Hello world"; }tốt hơn trong việc xử lý ghi nhiều giá trị luồng?
tổng kiểm tra

4
@checksum: Trong trường hợp cụ thể này, điều đó là không cần thiết vì không có lần đọc hoặc ghi valuenào diễn ra cùng một lúc. Nhưng, vâng, hãy luôn lưu ý đến thời điểm cần khóa.
Brian Gideon

Ý tưởng tuyệt vời! Hoạt động xuất sắc và phải là câu trả lời được chấp nhận.
MerseyViking

34

Nó phụ thuộc vào cách bạn muốn tạo luồng và phiên bản .NET có sẵn:

.NET 2.0+:

A) Bạn có thể tạo Threadđối tượng trực tiếp. Trong trường hợp này, bạn có thể sử dụng "close" - khai báo biến và nắm bắt nó bằng lambda-expression:

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

B) Bạn có thể sử dụng các đại diện và IAsyncResultvà trả về giá trị từ EndInvoke()phương thức:

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

C) Bạn có thể sử dụng BackgroundWorkerlớp học. Trong trường hợp này, bạn có thể sử dụng biến được capture (như với Threadđối tượng) hoặc xử lý RunWorkerCompletedsự kiện:

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

Bắt đầu với .NET 4.0, bạn có thể sử dụng Thư viện song song tác vụTasklớp để bắt đầu chuỗi của mình. Lớp chung Task<TResult>cho phép bạn nhận giá trị trả về từ thuộc Resulttính:

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

Bắt đầu với .NET 4.5, bạn cũng có thể sử dụng async/ awaittừ khóa để trả về giá trị trực tiếp từ tác vụ thay vì lấy thuộc Resulttính:

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

Lưu ý: phương thức có chứa đoạn mã trên được đánh dấu bằng asynctừ khóa.

Vì nhiều lý do, việc sử dụng Task Parallel Library là cách tốt hơn để làm việc với các luồng.


33

Tôi sẽ sử dụng cách tiếp cận BackgroundWorker và trả về kết quả trong e.Result.

BIÊN TẬP:

Điều này thường được kết hợp với WinForms và WPF, nhưng có thể được sử dụng bởi bất kỳ loại ứng dụng .NET nào. Đây là mã mẫu cho một ứng dụng bảng điều khiển sử dụng 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);
            }
        }
    }
}

Đầu ra:

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

CẬP NHẬT 2014

Xem câu trả lời của @ Roger bên dưới.

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

Ông chỉ ra rằng bạn có thể sử dụng Tác vụ trả về a Task<T>và kiểm tra Task<T>.Result.


Có, nhưng nó chỉ áp dụng cho WinForms và WPF.
Henk Holterman

@Henk: Không đúng. Tôi vừa viết một ứng dụng console đơn giản sử dụng BackgroundWorker chỉ để đảm bảo :-) Đã chỉnh sửa bài đăng của tôi bằng mã đó.
Eric J.

Eric, hãy đưa một số danh sách vào mã của bạn để xem khi nào điều gì xảy ra và trên ThreadId. Nó có thể không diễn ra như bạn mong đợi. (Đã hoàn thành sẽ chạy trước khi Dowork kết thúc và không chạy trên Main thread). Bgw cần một MessagePump.
Henk Holterman

@Henk: Bạn nói đúng một nửa. Đã hoàn thành chạy trên cùng một chuỗi với BackgroundWorker, nhưng nó chạy sau khi DoWork hoàn thành. Xem đầu ra trong câu trả lời đã chỉnh sửa.
Eric J.

2
Không có điều kiện chủng tộc, bởi vì chính xác một luồng đặt biến và chính xác một luồng đọc nó, và thứ tự chính xác của đặt so với đọc không quan trọng đối với việc thực thi đúng mã (nghĩa là điều kiện kết thúc có thể xảy ra trong luồng chính sớm hơn hoặc muộn hơn một chút tùy thuộc vào thứ tự các chủ đề được lên lịch, nhưng dù theo cách nào thì bạn vẫn nhận được kết quả chính xác).
Eric J.

21

Một chuỗi không phải là một phương thức - bạn thường không "trả về" một giá trị.

Tuy nhiên, nếu bạn đang cố gắng lấy lại một giá trị từ kết quả của một số quá trình xử lý, bạn có nhiều tùy chọn, hai tùy chọn chính là:

  • Bạn có thể đồng bộ hóa một phần dữ liệu được chia sẻ và đặt nó một cách thích hợp.
  • Bạn cũng có thể chuyển dữ liệu trở lại bằng một số hình thức gọi lại.

Nó thực sự phụ thuộc vào cách bạn đang tạo luồng và cách bạn muốn sử dụng nó, cũng như ngôn ngữ / khuôn khổ / công cụ bạn đang sử dụng.


15

Lớp yêu thích của tôi, chạy bất kỳ phương thức nào trên một luồng khác chỉ với 2 dòng mã.

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

sử dụng

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

Lưu ý rằng longFunctionComplete sẽ KHÔNG thực thi trên cùng một chuỗi như starthework.

Đối với các phương thức nhận tham số, bạn luôn có thể sử dụng bao đóng hoặc mở rộng lớp.


3
Không rõ ràng cho tất cả mọi người ... thứReturned ?, resultOfWork, PrintWorkComplete? vv
Lost_In_Library

14

Đây là một ví dụ đơn giản sử dụng một đại biểu ...

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

Có một loạt bài tuyệt vời về phân luồng tại Phân luồng trong C # .


9

Đơn giản chỉ cần sử dụng phương pháp ủy quyền.

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

Bây giờ tạo hàm Multiply sẽ hoạt động trên một chuỗi khác:

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

3
Tại sao câu trả lời dưới đây là "lưu nó vào một tệp văn bản và truy xuất nó"?
Jon

7

Tôi đã xem qua luồng này khi cũng cố gắng lấy giá trị trả về của một phương thức được thực thi trong một Luồng. Tôi nghĩ rằng tôi sẽ đăng giải pháp của tôi mà hoạt động.

Giải pháp này sử dụng một lớp để lưu trữ cả phương thức được thực thi (gián tiếp) và lưu trữ giá trị trả về. Lớp có thể được sử dụng cho bất kỳ hàm nào và bất kỳ kiểu trả về nào. Bạn chỉ cần khởi tạo đối tượng bằng kiểu giá trị trả về và sau đó chuyển hàm để gọi thông qua lambda (hoặc ủy nhiệm).


Triển khai 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();
    }
}

Để sử dụng mã này, bạn có thể sử dụng Lambda (hoặc một đại biểu). Đây là ví dụ sử dụng 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...
}

Thực hiện VB.NET 2008


Bất kỳ ai sử dụng VB.NET 2008 đều không thể sử dụng lambdas với các phương thức trả về không phải giá trị. Điều này ảnh hưởng đến ThreadedMethodlớp, vì vậy chúng tôi sẽ ExecuteMethodtrả về giá trị của hàm. Điều này không ảnh hưởng gì.

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

Với .NET Framework mới nhất, có thể trả về giá trị từ một luồng riêng biệt bằng Tác vụ, trong đó thuộc tính Kết quả chặn luồng gọi cho đến khi tác vụ kết thúc:

  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;

Để biết chi tiết, vui lòng xem http://msdn.microsoft.com/en-us/library/dd537613(v=vs.110).aspx


5

Các đại biểu ThreadStart trong C # được sử dụng để bắt đầu các chủ đề có kiểu trả về 'void'.

Nếu bạn muốn nhận 'giá trị trả về' từ một luồng, bạn nên ghi vào một vị trí được chia sẻ (theo cách an toàn cho luồng thích hợp) và đọc từ đó khi luồng đã hoàn thành việc thực thi.


5

Nếu bạn không muốn sử dụng BackgroundWorker và chỉ sử dụng một Thread thông thường, thì bạn có thể kích hoạt một sự kiện để trả về dữ liệu như sau:

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

    }
}

Tôi đã sửa một chi tiết nhỏ trong mã của bạn. Có vẻ như bạn đã bỏ qua thread1_phần đấu dây AsyncCompletedEventHandler của anh ấy . Nếu bản chỉnh sửa của tôi bị lỗi, vui lòng giúp tôi hiểu chuyện gì đang xảy ra ở đó.
jp2code

1
@ jp2code Bạn không thể làm thread1_Thread1Completed +=thread1_Thread1Completedlà tên của một hàm, vì vậy bạn không thể đặt nó ở bên trái của toán tử gán. Bên trái Thread1Completed +=được sử dụng vì đó là một sự kiện, vì vậy nó có thể xuất hiện ở bên trái của toán tử gán để thêm trình xử lý sự kiện. Xempublic event AsyncCompletedEventHandler Thread1Completed;
AaronLS

Tôi thấy nó bây giờ. Tôi không biết tại sao tôi không thể thấy trình xử lý sự kiện đó trong #regionphần của bạn trước đây. Tôi đã nhìn. Thật thà! :)
jp2code

2

Các chủ đề không thực sự có giá trị trả về. Tuy nhiên, nếu bạn tạo một ủy nhiệm, bạn có thể gọi nó không đồng bộ thông qua BeginInvokephương thức. Điều này sẽ thực thi phương thức trên một luồng nhóm luồng. Bạn có thể nhận được bất kỳ giá trị trả lại nào từ cuộc gọi chẳng hạn EndInvoke.

Thí dụ:

static int GetAnswer() {
   return 42;
}

...

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

GetAnswersẽ thực thi trên một chuỗi nhóm chủ đề và khi hoàn thành, bạn có thể lấy câu trả lời qua EndInvokenhư hình.


2

Các BackgroundWorker là tốt đẹp khi phát triển cho Windows Forms.

Giả sử bạn muốn vượt qua một lớp đơn giản qua lại:

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

Tôi đã viết một lớp học ngắn hạn làm như sau:

  • Tạo hoặc xóa danh sách
  • Bắt đầu một vòng lặp
  • Trong vòng lặp, hãy tạo một mục mới cho danh sách
  • Trong vòng lặp, hãy tạo một chuỗi
  • Trong vòng lặp, hãy gửi mục dưới dạng một tham số đến chuỗi
  • Trong vòng lặp, bắt đầu chuỗi
  • Trong vòng lặp, thêm chuỗi vào danh sách để xem
  • Sau vòng lặp, hãy tham gia từng chuỗi
  • Sau khi tất cả các phép nối đã hoàn tất, hãy hiển thị kết quả

Từ bên trong quy trình chuỗi:

  • Khóa cuộc gọi để mỗi lần chỉ có 1 chuỗi có thể tham gia quy trình này (những chuỗi khác phải đợi)
  • Đăng thông tin về mặt hàng.
  • Sửa đổi mặt hàng.
  • Khi chuỗi hoàn thành, dữ liệu được hiển thị trên bảng điều khiển.

Thêm một người được ủy quyền có thể hữu ích để đăng dữ liệu của bạn trực tiếp trở lại chuỗi chính của bạn, nhưng bạn có thể cần sử dụng Gọi nếu một số mục dữ liệu không an toàn cho chuỗi.

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

}

Để kiểm tra điều này, hãy tạo một Ứng dụng Bảng điều khiển nhỏ và đặt nó vào tệp 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);
        }
    }

}

Đây là ảnh chụp màn hình những gì tôi nhận được với cái này:

Đầu ra bảng điều khiển

Tôi hy vọng những người khác có thể hiểu những gì tôi đã cố gắng giải thích.

Tôi thích làm việc trên các chủ đề và sử dụng các đại biểu. Họ làm cho C # rất nhiều niềm vui.

Phụ lục: Dành cho Bộ mã hóa VB

Tôi muốn xem những gì liên quan đến việc viết mã trên dưới dạng Ứng dụng Bảng điều khiển VB. Việc chuyển đổi liên quan đến một số điều tôi không mong đợi, vì vậy tôi sẽ cập nhật chủ đề này ở đây cho những người muốn biết cách phân luồng trong 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";
    }
}

Ví dụ được cung cấp là sai, bạn thật may mắn vì nó phù hợp với bạn. Hãy tưởng tượng tình huống sau test(){ Thread.Sleep(5000); /*Highly time demanding process*/ return "Returned from test()";}. Trong trường hợp này, luồng độc lập sẽ không có thời gian để gán giá trị mới cho returnValuebiến. Phương án cuối cùng, bạn có thể lưu một tham chiếu luồng var standaloneThread = new Thread(()=> //...);và sau đó, khởi động nó một cách đồng bộ standaloneThread.Start(); standaloneThread.Join();. Nhưng đây chắc chắn không phải là cách thực hành tốt nhất.
AlexMelw

1

Một giải pháp đơn giản là truyền một tham số bằng ref đến hàm đang chạy trong luồng và thay đổi giá trị của nó trong luồng.

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

Nếu bạn không thể thay đổi chức năng đang chạy trong lốp, bạn có thể bọc nó một chức năng khác:

 bool theirFunction(var someVar){
   return false;
}


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

vui lòng giải thích downvote. Tôi sử dụng mẫu này trong mã của riêng mình và nó hoạt động hoàn toàn tốt.
CodeToad vào

0

Có thể sử dụng Mã này:

 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

Tôi không phải là loại chuyên gia về phân luồng, đó là lý do tại sao tôi đã làm như thế này:

Tôi đã tạo tệp Cài đặt và

Bên trong chuỗi mới:

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

Sau đó, tôi nhận giá trị đó bất cứ khi nào tôi cần.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.