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

Просто, потому что что-то - объект, это не означает, что это всегда должно быть широко открытым для всех для расширения, или переопределять это - поведение.

А хорошая аналогия была бы дверью и блокировкой. Характер двери состоит в том, чтобы позволить людям для прохождения через него, это - то, для чего это создается. Все же большинство дверей разработано с блокировками, которые могут ограничить способность людей пройти через них. Решение, имеет ли дверь блокировку и заблокирована ли та блокировка по умолчанию, оставлено архитектору комнаты, на основе того, что находится в комнате и у кого должен быть доступ к нему, не на том, что это - дверь.

, Конечно, может быть печально, если большинство дверей, в особенности создающих, заблокировано по умолчанию, особенно если существует тот, который Вы действительно хотите пройти.:-) Я был там мной, и я задал тот же вопрос. Короткий ответ - то, что иногда при разработке сложной платформы, люди склонны допускать ошибку немного на более осторожной стороне и изолировать классы по умолчанию, если нет явный сценарий, который требует, чтобы они были расширены.

95
задан Sam 22 June 2014 в 15:45
поделиться

7 ответов

На самом деле потоки не имеют возвращаемых значений. Однако, если вы создаете делегат, вы можете вызывать его асинхронно с помощью метода 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
ответ дан 24 November 2019 в 05:46
поделиться

Если вы не хотите использовать 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

    }
}
5
ответ дан 24 November 2019 в 05:46
поделиться

Вот простой пример с использованием делегата ...

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

14
ответ дан 24 November 2019 в 05:46
поделиться

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

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

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

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

21
ответ дан 24 November 2019 в 05:46
поделиться

Я бы использовал подход 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, который возвращает Task , и проверить Task .Result .

33
ответ дан 24 November 2019 в 05:46
поделиться

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

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

5
ответ дан 24 November 2019 в 05:46
поделиться

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

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.

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

13
ответ дан 24 November 2019 в 05:46
поделиться
Другие вопросы по тегам:

Похожие вопросы: