Повернення значення з потоку?


Відповіді:


94

Один з найпростіших способів отримати повернене значення з потоку - це використовувати замикання. Створіть змінну, яка буде містити повернене значення з потоку, а потім захоплюйте її в лямбда-вираз. Призначте значення "return" цій змінній з робочого потоку, а після закінчення цього потоку ви можете використовувати його з батьківського потоку.

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
Не lock(value) { value = "Hello world"; }було б краще в роботі з написанням кількох потоків?
контрольна сума

4
@checksum: У цьому конкретному випадку це зайве, оскільки не valueвідбувається одночасного читання чи запису . Але, так, завжди пам’ятайте про те, коли необхідний замок.
Брайан Гедеон

Фантастична ідея! Працює блискуче, і це має бути прийнятою відповіддю.
MerseyViking

34

Це залежить від того, як ви хочете створити нитку та доступну версію .NET:

.NET 2.0+:

А) Ви можете створити Threadоб'єкт безпосередньо. У цьому випадку ви можете використовувати "closure" - оголосити змінну і захопити її за допомогою лямбда-виразу:

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

В) Ви можете використовувати 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клас для запуску потоків. Загальний клас 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ключовим словом.

З багатьох причин використання Бібліотеки паралельних завдань є кращим способом роботи з потоками.


33

Я б застосував підхід 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 року

Дивіться відповідь @ Роджера нижче.

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

Він вказує, що ви можете використовувати завдання, яке повертає а Task<T>, і перевірити Task<T>.Result.


Так, але це стосується лише WinForms та WPF.
Хенк Холтерман

@Henk: Неправда. Я щойно написав простий консольний додаток, який використовує BackgroundWorker лише для того, щоб переконатися :-) Редагував мою публікацію цим кодом.
Ерік Дж.

Еріку, поклади в свій код кілька записів, щоб побачити, що відбувається, і на чому ThreadId. Це може не піти так, як ви очікуєте. (Виконане буде запущено до завершення роботи, а не на головній темі). Bgw потребує MessagePump.
Хенк Холтерман

@Henk: Ти напівправий. Виконаний запуск на тій самій нитці, що і BackgroundWorker, але він запускається після завершення DoWork. Дивіться вихід у редагованій відповіді.
Ерік Дж.

2
Умови гонки немає, тому що саме один потік встановлює змінну і рівно один потік читає її, а точний порядок набору проти читання не має значення для правильного виконання коду (тобто умова завершення може статися в головному потоці трохи раніше чи пізніше, залежно від порядку, в якому заплановані теми, але в будь-якому випадку ви все одно отримаєте правильний результат).
Ерік Дж.

21

Нитка не є методом - ви зазвичай не повертаєте значення.

Однак якщо ви намагаєтеся отримати значення назад за результатами деякої обробки, у вас є багато варіантів, два основні з них:

  • Ви можете синхронізувати спільний фрагмент даних та встановити його відповідним чином.
  • Ви також можете передавати дані назад у якійсь формі зворотного дзвінка.

Це дійсно залежить від того, як ви створюєте нитку та як ви хочете її використовувати, а також від мови / рамки / інструментів, якими ви користуєтесь.


15

Мій улюблений клас, запускає будь-який метод на інший потік із лише 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 НЕ виконуватиметься в тій самій нитці, що і початкова робота.

Для методів, які приймають параметри, ви завжди можете закрити або розширити клас.


3
Не всім зрозуміло ... Повернення ?, РезультатРобота, ДрукРобота завершено? пр.
Lost_In_Library

14

Ось простий приклад використання делегата ...

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 в C # .


9

Просто використовуйте делегатський підхід.

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

Тепер зробіть функцію множення, яка буде працювати на іншій нитці:

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

3
Чому ця відповідь нижче "збережіть її у текстовому файлі та отримайте її"?
Джон

7

Я натрапив на цей потік і при спробі отримати повернене значення методу, який виконується в потоці. Я думав, що опублікую своє рішення, яке працює.

Це рішення використовує клас для зберігання методу, який буде виконуватися (побічно), і зберігає повернене значення. Клас можна використовувати для будь-якої функції та будь-якого типу повернення. Ви просто інстанціюєте об'єкт, використовуючи тип повернутого значення, а потім передаєте функцію для виклику через лямбда (або делегат).


Впровадження 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();
    }
}

Для використання цього коду можна використовувати лямбда (або делегата). Ось приклад використання лямбда:

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


Кожен, хто використовує VB.NET 2008, не може використовувати лямбдаси з нецінні методи повернення. Це впливає на ThreadedMethodклас, тому ми змусимо ExecuteMethodповернути значення функції. Це нічого не шкодить.

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

За допомогою останньої .NET Framework можна повернути значення з окремого потоку за допомогою завдання, де властивість Result блокує викликовий потік до завершення завдання:

  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;

Детальніше див. На веб-сторінці http://msdn.microsoft.com/en-us/library/dd537613(v=vs.110).aspx


5

Делегати ThreadStart в C #, які використовуються для запуску потоків, мають тип повернення 'void'.

Якщо ви хочете отримати "повернути значення" з потоку, вам слід написати до спільного місця (відповідним способом, безпечним для потоків) і прочитати з нього, коли потік завершив виконання.


5

Якщо ви не хочете використовувати BackgroundWorker, а просто використовувати звичайну нитку, ви можете запустити подію, щоб повернути такі дані:

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

    }
}

Я зафіксував незначну деталь у вашому коді. Схоже, ви залишили thread1_частину на його проводці AsyncCompletedEventHandler . Якщо моя редакція була помилкою, будь ласка, допоможіть мені зрозуміти, що там відбувається.
jp2code

1
@ jp2code Ви не можете зробити, thread1_Thread1Completed +=оскільки thread1_Thread1Completedце ім'я функції, тому ви не можете розмістити її в лівій частині оператора призначення. Ліва сторона Thread1Completed +=використовується тому, що це подія, тому вона може з'являтися на лівій стороні оператора призначення, щоб додати обробники подій. Дивітьсяpublic event AsyncCompletedEventHandler Thread1Completed;
AaronLS

Я бачу це зараз. Я не знаю, чому я раніше не зміг побачити цього обробника подій у вашому #regionрозділі. Я подивився. Чесно! :)
jp2code

2

Нитки насправді не мають повернених значень. Однак якщо ви створили делегата, ви можете викликати його асинхронно за допомогою BeginInvokeметоду. Це виконає метод на потоці пулу потоків. Ви можете отримати будь-яке повернене значення, наприклад, дзвінок через EndInvoke.

Приклад:

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буде виконано на потоці пулу потоків, і після завершення ви можете отримати відповідь, EndInvokeяк показано на малюнку.


2

BackgroundWorker хороша при розробці для Windows Forms.

Скажіть, що ви хочете проходити простий клас туди і назад:

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

Я написав короткий клас, який робить наступне:

  • Створіть або очистіть список
  • Почніть цикл
  • У циклі створіть новий елемент для списку
  • У циклі створіть нитку
  • У циклі відправте елемент як параметр до потоку
  • У циклі запустіть нитку
  • У циклі додайте нитку до списку для перегляду
  • Після циклу з'єднайте кожну нитку
  • Після завершення всіх приєднань відобразити результати

Зсередини потоки потоку:

  • Блокуйте виклик, щоб лише 1 нитка могла входити в цю процедуру одночасно (інші повинні чекати)
  • Опублікуйте інформацію про предмет.
  • Змініть елемент.
  • Коли нитка завершується, дані відображаються на консолі.

Додавання делегата може бути корисним для публікації ваших даних безпосередньо в основний потік, але вам може знадобитися використовувати Invoke, якщо деякі елементи даних не є безпечними для потоків.

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

}

Щоб перевірити це, створіть невелику програму консолі та помістіть її у файл 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);
        }
    }

}

Ось скріншот того, що я отримав із цим:

Вихід консолі

Сподіваюся, інші можуть зрозуміти, що я намагався пояснити.

Мені подобається працювати над темами та використовувати делегати. Вони роблять C # дуже весело.

Додаток: Для кодерів VB

Я хотів побачити, що було пов'язано з написанням коду вище як додатка консолі VB. Конверсія включала декілька речей, які я не очікував, тому я оновлю цю тему тут для тих, хто хоче знати, як провести тему в 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";
    }
}

Наведений приклад помиляється, вам пощастило, що він працював на вас. Уявіть таку ситуацію test(){ Thread.Sleep(5000); /*Highly time demanding process*/ return "Returned from test()";}. У цьому випадку окрема нитка не встигла б призначити нове значення returnValueзмінній. В крайньому випадку ви можете зберегти посилання на потік var standaloneThread = new Thread(()=> //...);і після цього запустити його синхронізовано standaloneThread.Start(); standaloneThread.Join();. Але це, звичайно, не найкраща практика.
AlexMelw

1

Просте рішення - передати параметр шляхом посилання на функцію, що працює в потоці, і змінити його значення в потоці.

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

Якщо ви не можете змінити функцію, що працює в протекторі, ви можете переключити її на іншу функцію:

 bool theirFunction(var someVar){
   return false;
}


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

поясніть, будь ласка, протокол. Я використовую цей зразок у власному коді, і він прекрасно працює.
CodeToad

0

Можна використовувати цей код:

 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

Я не є фахівцем з нитки, тому я зробив це так:

Я створив файл налаштувань і

Всередині нової нитки:

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

Тоді я підбираю це значення, коли мені це потрібно.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.