проблема асинхронности и ожидания в многопоточном коде - PullRequest
0 голосов
/ 11 июля 2019

У меня такая простая ситуация:

public partial class Form1 : Form {
  Task t;
  public Form1() {
     InitializeComponent();
     t = Prepare();
  }

  public Task waitT() {
     return Task.Factory.StartNew(() => {
        for (int i = 0; i < 5; i++) {
           Thread.Sleep(1000);
           Console.WriteLine("T works \n");
        }
     });
  }
  public Task waitT2() {
     return Task.Factory.StartNew(() => {
        for (int i = 0; i < 3; i++) {
           Thread.Sleep(1000);
           Console.WriteLine("T2 works \n");
        }
     });
  }

  public Task waitT3() {
     return Task.Factory.StartNew(() => {
        for (int i = 0; i < 2; i++) {
           Thread.Sleep(1000);
           Console.WriteLine("T3 works \n");
        }
     });
  }

  public async Task Prepare() {
     //await Task.Factory.StartNew(async () => {
        await waitT();
        Console.WriteLine("T DOne.");
        await waitT2();
        Console.WriteLine("T2 DOne.");
        await waitT3();
        Console.WriteLine("T3 DOne.");
    // });
  }

  protected override void OnPaint(PaintEventArgs e) {
     base.OnPaint(e);
     t.Wait();
     Console.WriteLine("Finish");
  }

}

В нескольких словах я хочу запустить задачу в конструкторе и подождать некоторые данные в событиях Paint.Проблема в том, что выполнение в методе wait () блокируется, а код Console.WriteLine ("T DOne.") Никогда не вызывается.Я читал, что выполнение асинхронных методов выполняется впервые, и я думаю, что проблема заключается в следующем.Чтобы попытаться решить проблему, я изменил свой код функции, подготовившись следующим образом:

 public async Task Prepare() {
     return await Task.Factory.StartNew(async () => {
        await waitT();
        Console.WriteLine("T DOne.");
        await waitT2();
        Console.WriteLine("T2 DOne.");
        await waitT3();
        Console.WriteLine("T3 DOne.");
     });
  }

Таким образом, ситуация меняется, и метод ожидания в рисовании не учитывается, и выполнение сразу передаетсяПроцесс Console.WriteLine («Finish») и Prepare выполняется до завершения.

Возможно, я совершаю концептуальную ошибку, кто-то может объяснить мне правильный подход к этому?

Спасибо

Ответы [ 2 ]

0 голосов
/ 11 июля 2019

[EDIT] Я изменил свой код, я удалил назначение задачи из конструктора и переместил его в событие загрузки:

   public partial class Form1 : Form {
  Task t;
  public Form1() {
     InitializeComponent();
  }

  protected override void OnLoad(EventArgs e) {
     base.OnLoad(e);
     t = Prepare();
  }

  public Task waitT() {
     return Task.Run(() => {
        for (int i = 0; i < 5; i++) {
           Thread.Sleep(1000);
           Console.WriteLine("T works \n");
        }
     });
  }
  public Task waitT2() {
     return Task.Run(() => {
        for (int i = 0; i < 3; i++) {
           Thread.Sleep(1000);
           Console.WriteLine("T2 works \n");
        }
     });
  }

  public Task waitT3() {
     return Task.Run(() => {
        for (int i = 0; i < 2; i++) {
           Thread.Sleep(1000);
           Console.WriteLine("T3 works \n");
        }
     });
  }

  public async Task Prepare() {
     await Task.Run(async () => {
        await waitT();
        Console.WriteLine("T DOne.");
        await waitT2();
        Console.WriteLine("T2 DOne.");
        await waitT3();
        Console.WriteLine("T3 DOne.");
     });
  }

  protected override void OnPaint(PaintEventArgs e) {
     base.OnPaint(e);
     t.Wait();
     Console.WriteLine("Finish");
  }

}

Теперь результат другой, я жду правильных методов end 3 wait, но выполнение остается заблокированным в t.wait (), несмотря на конец 3 методов .... Console.WriteLine ( "Конец"); не называется: (

[РЕДАКТИРОВАТЬ 2] Возможно, я решил свою проблему, и я могу использовать другой подход для создания задачи в конструкторе, этот код работает нормально (это только и пример с событием):

   public partial class Form1 : Form {
  Task t;
  public Form1() {
     InitializeComponent();
     t = Prepare();
  }

  protected override void OnLoad(EventArgs e) {
     base.OnLoad(e);
  }

  public Task waitT() {
     return Task.Run(() => {
        for (int i = 0; i < 5; i++) {
           Thread.Sleep(1000);
           Console.WriteLine("T works \n");
        }
     });
  }
  public Task waitT2() {
     return Task.Run(() => {
        for (int i = 0; i < 3; i++) {
           Thread.Sleep(1000);
           Console.WriteLine("T2 works \n");
        }
     });
  }

  public Task waitT3() {
     return Task.Run(() => {
        for (int i = 0; i < 2; i++) {
           Thread.Sleep(1000);
           Console.WriteLine("T3 works \n");
        }
     });
  }

  public Task Prepare() {
     return Task.Run(async () => {
        await waitT();
        Console.WriteLine("T DOne.");
        await waitT2();
        Console.WriteLine("T2 DOne.");
        await waitT3();
        Console.WriteLine("T3 DOne.");
     });
  }

  protected override void OnPaint(PaintEventArgs e) {
     base.OnPaint(e);
     t.Wait();
     Console.WriteLine("Finish");
  }

}

0 голосов
/ 11 июля 2019

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

public Form1() {
     InitializeComponent();
     Task.WaitAll(Task.Run( async ()=> await Prepare()));
}

Вам не нужна переменная Task t. Поскольку Prepare не возвращает никаких возвращаемых значений, вы можете просто дождаться его. Я полагаю, вы сделали этот пример, чтобы представить некоторую проблему, которая у вас есть Обычно, если вы начинаете с async и await, вам нужно накачать его до самого высокого уровня графического интерфейса. Любое прерывание синхронной обработки приведет к тупику

EDIT: После вашего комментария:

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

Я бы предложил запустить задачу в конструкторе:

t = Task.Run(async ()=> await Prepare());

и ждите его в событии OnPaint, которое вы должны сделать async, чтобы он не блокировал пользовательский интерфейс:

protected override async void OnPaint(PaintEventArgs e)
{
    base.OnPaint(e);
    await t;
    Console.WriteLine($"Finish");
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...