как заставить ожидание продолжить в том же потоке вызывающего в консольном приложении - PullRequest
0 голосов
/ 01 июня 2018

Я пишу консольное приложение .Net на C #.

Мне нужно преобразовать основанный на событиях поточный вызов в асинхронный метод.

Например:

public class WorkerClass
    {
        public EventHandler Progress;
        public EventHandler Finished;
        public void DoSomething()
        {
            Thread td = new Thread(Start);
            td.Name = "Worker Thread";
            td.Start();
        }

        private void Start()
        {
            int i = 0;
            while (i < 10)
            {
                Thread.Sleep(1000);
                i++;
                var progress = i * 10;
                Progress?.Invoke((object)progress, null);
            }
            Finished?.Invoke(null, null);
        }
    }



    Class MyMainClass
            {
                private static WorkerClass myWorkerClass;
                static void Main(string[] args)
                {
                    myWorkerClass = new WorkerClass();
                    RunAsynchronously(null);
                }
                private async void RunAsynchronously(EventHandler progress)        
                {           
                    //Main Thread Call

                    await DoSomethingAsync(progress);

                    //Resume after worker finished is not in main thread? :(
                    //Needed main thread to continue
                }
                private Task<int> DoSomethingAsync(EventHandler progress)
                {
                    var completionSource = new TaskCompletionSource<int>();

                    EventHandler Progress = null;
                    EventHandler Finished = null;

                    Finished = (o, e) =>
                    {
                        myWorkerClass.Finished -= Finished;
                        myWorkerClass.Progress -= Progress;
                        completionSource.SetResult(100);
                    };

                    Progress = (o, e) => { progress?.Invoke(o, e); };

                    myWorkerClass.Finished += Finished;
                    myWorkerClass.Progress += Progress;

                    myWorkerClass.DoSomething();            

                    return completionSource.Task;
                }
            }

Из-за некоторых ограничений мне нужно, чтобы основной поток продолжался, однако после того, как await возобновляет метод RunAsynchronously, текущий поток чем-то отличается от основного потока (или потока, который вызвал await).

Я знаю это поведениебудет автоматически работать, если вызывающая сторона является потоком пользовательского интерфейса, но в моем случае это консольное приложение, и у меня нет пользовательского интерфейса.

Пожалуйста, предложите решение.Заранее спасибо.

1 Ответ

0 голосов
/ 04 июня 2018

Мне кажется, что у вас есть фундаментальное неправильное понимание того, что ожидать от консольного приложения и модели потоков .NET-приложения.

Консольное приложение работает до тех пор, пока его поток ввода не завершит выполнение Mainметод.Это означает, что вы либо заняты потоком, выполняя работу, либо блокируете поток, пока другой сигнал не разблокирует его и не позволит методу завершиться.Это очень простая конструкция, предназначенная для того, чтобы позволить вам запустить приложение, выполнить некоторую работу, а затем завершить работу, когда вы закончите.Это означает, что поток никогда не «свободен» для выполнения другой работы;Вы не можете запланировать выполнение задачи в этом потоке без полного изменения модели планирования работ на более сложную.

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


Такого рода путаница заключается в том, почему шаблон async void обычно считается анти-шаблоном, и, конечно, почемудопустимо использовать только в приложениях с пользовательским интерфейсом.Приложение работает корректно только из-за очень специфической модели планирования задач.Любая другая модель вызывает разрушение этой конструкции, поглощая исключения и делая невозможным узнать, когда работа завершена.

...