Как вернуть значение из потока?
Возврат значения из потока?
Ответ 1
Одним из самых простых способов получить возвращаемое значение из потока является использование закрытий. Создайте переменную, которая будет удерживать возвращаемое значение из потока, а затем захватит его в выражении лямбда. Назначьте значение "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
}
Ответ 2
Я бы использовал метод BackgroundWorker и вернул результат в e.Result.
EDIT:
Это обычно связано с 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.
fooobar.com/questions/72972/...
Он указывает, что вы можете использовать задачу, которая возвращает Task<T>
, и отметьте Task<T>.Result
.
Ответ 3
Это зависит от того, как вы хотите создать поток и доступную версию .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);
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
для запуска ваших потоков. Общий класс 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
.
По многим причинам использование параллельной библиотеки задач является предпочтительным способом работы с потоками.
Ответ 4
Нить не является методом - вы обычно не возвращаете значение.
Однако, если вы пытаетесь извлечь значение из результатов какой-либо обработки, у вас есть много вариантов, а два основных:
- Вы можете синхронизировать общий фрагмент данных и соответствующим образом настроить его.
- Вы также можете передать данные в какой-либо форме обратного вызова.
Это зависит от того, как вы создаете поток, и как вы хотите его использовать, а также язык/рамки/инструменты, которые вы используете.
Ответ 5
Вот простой пример использования делегата...
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 в С#.
Ответ 6
Мой любимый класс, запускает любой метод в другом потоке только с двумя строками кода.
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.
Для методов, которые принимают параметры, вы всегда можете использовать закрытие или развернуть класс.
Ответ 7
Я наткнулся на этот поток, когда также пытался получить возвращаемое значение метода, который выполняется в потоке. Я думал, что опубликую свое решение, которое будет работать.
Это решение использует класс для хранения как метода, который будет выполняться (косвенно), так и сохраняет возвращаемое значение. Класс может использоваться для любой функции и любого возвращаемого типа. Вы просто создаете экземпляр объекта, используя тип возвращаемого значения, а затем передаете функцию для вызова через лямбда (или делегат).
Реализация С# 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();
}
}
Чтобы использовать этот код, вы можете использовать Лямбду (или делегат). Вот пример использования 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
Любой, кто использует VB.NET 2008, не может использовать lambdas с не возвращающими значение методами. Это влияет на класс ThreadedMethod
, поэтому мы сделаем ExecuteMethod
значение value. Это ничего не болит.
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
Ответ 8
В последней платформе .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
Ответ 9
Если вы не хотите использовать 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 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
}
}
Ответ 10
Представители ThreadStart в С#, используемые для запуска потоков, имеют тип возврата 'void'.
Если вы хотите получить "возвращаемое значение" из потока, вы должны записать его в общую папку (в соответствии с потокобезопасным способом) и прочитать это, когда поток завершил выполнение.
Ответ 11
Просто используйте подход делегата.
int val;
Thread thread = new Thread(() => { val = Multiply(1, 2); });
thread.Start();
Теперь сделайте функцию Multiply, которая будет работать в другом потоке:
int Multiply(int x, int y)
{
return x * y;
}
Ответ 12
В потоках действительно нет возвращаемых значений. Однако, если вы создадите делегат, вы можете вызвать его асинхронно с помощью метода 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
, как показано.
Ответ 13
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; }
}
Я написал короткий класс, который делает следующее:
- Создать или удалить список
- Запустить цикл
- В цикле создайте новый элемент для списка
- В цикле создайте поток
- В цикле отправьте элемент в качестве параметра в поток
- В цикле, запустите поток
- В цикле добавьте поток в список для просмотра
- После цикла присоедините каждый поток
- После того, как все соединения завершены, отобразите результаты
Изнутри процедуры потока:
- Блокировка вызовов, чтобы только один поток мог вводить эту процедуру за раз (другие должны ждать)
- Опубликовать информацию о товаре.
- Измените элемент.
- Когда поток завершается, данные отображаются на консоли.
Добавление делегата может быть полезно для публикации ваших данных непосредственно в ваш основной поток, но вам может потребоваться использовать Вызывать, если некоторые элементы данных не являются потокобезопасными.
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);
}
}
}
Вот скриншот того, что я получил от этого:
Я надеюсь, что другие могут понять, что я пытался объяснить.
Мне нравится работать с потоками и использовать делегатов. Они делают С# очень весело.
Приложение: для кодеров 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
Ответ 14
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";
}
}
Ответ 15
Простое решение - передать параметр ref в функцию, которая выполняется в потоке, и изменить ее значение в потоке.
// 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);
}
Ответ 16
Может использовать этот код:
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;
}
Ответ 17
Я не специалист по потоковому использованию, поэтому я сделал это так:
Я создал файл настроек и
Внутри нового потока:
Setting.Default.ValueToBeSaved;
Setting.Default.Save();
Затем я подбираю это значение всякий раз, когда мне это нужно.
Патрик