Возвращаете значение из потока?

Ответы:

94

Один из самых простых способов получить возвращаемое значение из потока - использовать замыкания. Создайте переменную, которая будет содержать возвращаемое значение из потока, а затем зафиксируйте его в лямбда-выражении. Присвойте этой переменной «возвращаемое» значение из рабочего потока, а затем, когда этот поток завершится, вы можете использовать его из родительского потока.

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

A) Вы можете создать Threadобъект напрямую. В этом случае вы можете использовать «закрытие» - объявить переменную и зафиксировать ее с помощью лямбда-выражения:

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

Б) Вы можете использовать делегаты 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класс для запуска ваших потоков. Общий класс 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 ГОДА

См. Ответ @ Roger ниже.

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

Он указывает, что вы можете использовать Task, который возвращает a Task<T>, и проверять Task<T>.Result.

Эрик Дж.
источник
Да, но это применимо только к WinForms и WPF.
Хенк Холтерман,
@Henk: Неправда. Я просто написал простое консольное приложение, которое использует BackgroundWorker, чтобы убедиться :-) Отредактировал свое сообщение этим кодом.
Эрик Дж.
Эрик, добавьте в код несколько строк записи, чтобы узнать, когда что произойдет и с каким ThreadId. Все может пойти не так, как вы ожидали. (Завершено будет выполняться до завершения Dowork, а не в основном потоке). Bgw нужен MessagePump.
Хенк Холтерман,
@Henk: Ты наполовину прав. Completed запускается в том же потоке, что и BackgroundWorker, но запускается после завершения DoWork. См. Вывод в отредактированном ответе.
Эрик Дж.
2
Состояние гонки отсутствует, потому что ровно один поток устанавливает переменную и ровно один поток читает ее, и точный порядок установки и чтения не имеет значения для правильного выполнения кода (т.е. условие завершения может возникнуть в основном потоке. немного раньше или позже в зависимости от порядка, в котором запланированы потоки, но в любом случае вы все равно получите правильный результат).
Эрик Дж.
21

Поток - это не метод - обычно вы не «возвращаете» значение.

Однако, если вы пытаетесь получить значение из результатов некоторой обработки, у вас есть много вариантов, два основных из которых:

  • Вы можете синхронизировать совместно используемый фрагмент данных и установить его соответствующим образом.
  • Вы также можете передать данные обратно в той или иной форме обратного вызова.

Это действительно зависит от того, как вы создаете поток и как вы хотите его использовать, а также от языка / фреймворка / инструментов, которые вы используете.

Рид Копси
источник
15

Мой любимый класс, запускает любой метод в другом потоке всего с двумя строками кода.

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.

Для методов, которые принимают параметры, вы всегда можете использовать замыкания или расширить класс.

Эрик
источник
3
Не для всех понятно ... stuffReturned ?, resultOfWork, PrintWorkComplete? и т. д.
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 # есть потрясающая серия статей о многопоточности .

JP Alioto
источник
10

Просто используйте делегированный подход.

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

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

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

Чтобы использовать этот код, вы можете использовать Lambda (или делегат). Вот пример использования лямбда-выражений:

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 можно возвращать значение из отдельного потока с помощью Task, где свойство 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

user8128167
источник
5

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

Если вы хотите получить «возвращаемое значение» из потока, вы должны записать в совместно используемое место (подходящим потокобезопасным способом) и читать из него, когда поток завершит выполнение.

jscharf
источник
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

    }
}
AaronLS
источник
Я исправил мелкую деталь в вашем коде. Похоже, вы остановили thread1_часть его подключения AsyncCompletedEventHandler . Если мое редактирование было ошибочным, пожалуйста, помогите мне понять, что там происходит.
jp2code
1
@ jp2code Вы не можете этого сделать, thread1_Thread1Completed +=потому чтоthread1_Thread1Completed это имя функции, поэтому вы не можете поместить его в левую часть оператора присваивания. Левая часть Thread1Completed +=используется, потому что это событие, поэтому она может отображаться слева от оператора присваивания для добавления обработчиков событий. Смотритеpublic event AsyncCompletedEventHandler Thread1Completed;
AaronLS
Теперь я это вижу. Я не знаю, почему я #regionраньше не мог видеть этот обработчик событий в вашем разделе. Я посмотрел. Честный! :)
jp2code
2

На самом деле потоки не имеют возвращаемых значений. Однако, если вы создаете делегат, вы можете вызывать его асинхронно с помощью BeginInvokeметода. Это выполнит метод в потоке пула потоков. Вы можете получить любое возвращаемое значение, например, call via 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
jp2code
источник
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
источник
пожалуйста, объясните отрицательный голос. Я использую этот шаблон в своем собственном коде, и он отлично работает.
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();

Затем я беру это значение всякий раз, когда мне это нужно.

Патрик
источник