[c#] 스레드에서 값을 반환합니까?

스레드에서 값을 어떻게 반환합니까?



답변

스레드에서 반환 값을 얻는 가장 쉬운 방법 중 하나는 클로저를 사용하는 것입니다. 스레드의 반환 값을 저장할 변수를 만든 다음 람다 식으로 캡처합니다. 작업자 스레드에서이 변수에 “반환”값을 할당 한 다음 해당 스레드가 종료되면 부모 스레드에서 사용할 수 있습니다.

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
}


답변

스레드 생성 방법과 사용 가능한 .NET 버전에 따라 다릅니다.

.NET 2.0 이상 :

A) Thread개체를 직접 만들 수 있습니다 . 이 경우 “closure”를 사용할 수 있습니다. 변수를 선언하고 lambda-expression을 사용하여 캡처합니다.

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

B) 대리자를 사용하고 메서드 IAsyncResult에서 값을 반환 할 수 있습니다 EndInvoke().

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

C) BackgroundWorker수업 을 사용할 수 있습니다 . 이 경우 캡처 된 변수 (예 : Thread객체 포함)를 사용하거나 RunWorkerCompleted이벤트를 처리 할 수 있습니다 .

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 이상 :

.NET 4.0부터는 Task Parallel LibraryTask클래스를 사용하여 스레드를 시작할 수 있습니다. 일반 클래스를 Task<TResult>사용하면 Result속성 에서 반환 값을 가져올 수 있습니다 .

//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 이상 :

.NET 4.5부터는 속성 을 얻는 대신 async/ await키워드를 사용 하여 작업에서 직접 값을 반환 할 수도 있습니다 Result.

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

참고 : 위의 코드가 포함 된 메서드는 async키워드 로 표시됩니다 .

여러 가지 이유로 Task Parallel Library를 사용하는 것이 스레드 작업에 선호되는 방법입니다.


답변

BackgroundWorker 접근 방식을 사용하고 결과를 e.Result에 반환합니다.

편집하다:

이것은 일반적으로 WinForms 및 WPF와 관련이 있지만 모든 유형의 .NET 응용 프로그램에서 사용할 수 있습니다. 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);
            }
        }
    }
}

산출:

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

2014 업데이트

아래 @Roger의 답변을 참조하십시오.

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

그는를 반환 Task<T>하고를 확인 하는 Task를 사용할 수 있다고 지적합니다 Task<T>.Result.


답변

스레드는 메소드가 아닙니다. 일반적으로 값을 “반환”하지 않습니다.

그러나 일부 처리 결과에서 값을 다시 가져 오려는 경우 다음과 같은 두 가지 주요 옵션이 있습니다.

  • 공유 데이터를 동기화하고 적절하게 설정할 수 있습니다.
  • 또한 어떤 형태의 콜백으로 데이터를 다시 전달할 수도 있습니다.

스레드를 만드는 방법, 사용하는 방법, 사용중인 언어 / 프레임 워크 / 도구에 따라 다릅니다.


답변

내가 가장 좋아하는 클래스는 단 2 줄의 코드로 다른 스레드에서 모든 메서드를 실행합니다.

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

용법

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

longFunctionComplete는 starthework와 동일한 스레드에서 실행되지 않습니다.

매개 변수를받는 메소드의 경우 항상 클로저를 사용하거나 클래스를 확장 할 수 있습니다.


답변

다음은 대리자를 사용하는 간단한 예입니다.

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

Threading in C # 에는 스레딩에 대한 훌륭한 시리즈가 있습니다 .


답변

위임 방식을 사용하기 만하면됩니다.

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

이제 다른 스레드에서 작동하는 Multiply 함수를 만듭니다.

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