1

Есть код:

public async Task Async() {
  var T = new List < string > ();
  for (int i = 0; i < 10000; i++) {
    T.Add(i.ToString());
  }
  tt.ItemsSource = T;
}

private async void Window_Loaded(object sender, RoutedEventArgs e) {
  await Async();
}

Все выполняется, но он все равно блокирует окна, и с ним нельзя взаимодействовать, пока не выполнится метод.

Подскажите, как правильно сделать?

Cheg
  • 10,486
Саске
  • 1,326
  • 13
  • 23

3 Answers3

1
public async Task Async()
{
     tt.ItemsSource = await Task.Run(() =>
     {
          var T = new List<string>();
          for (int i = 0; i < 10000; i++)
          {
               T.Add(i.ToString());
          }
          return T;
     });
}
Alexsandr Ter
  • 873
  • 5
  • 14
  • все равно при запуске окна не могу немогуне чего делать пока listbox незаполнится – Саске Jul 07 '17 at 17:35
  • Это нормально, так после запуска Task поток освобождается и захватывается когда Task возвращает результат. То о чем вы говорите другая задача – Alexsandr Ter Jul 07 '17 at 18:58
  • Вы не правильно используете ключевые слова, окно зависает не от того что Task запущен, а от того что вы делаете это синхронно. Выше дал ответ. –  Jul 07 '17 at 19:05
  • @Birdy: Странно, а что не правильно в этом ответе? Там же Task.Run, вроде всё в порядке. – VladD Jul 07 '17 at 19:22
  • @VladD, Если посмотреть что будет на выходе, то непонятно зачем так сделано, и все естественно зависнет `private async void Window_Loaded(object sender, RoutedEventArgs e) { await Async(); }

    public async Task Async() { tt.ItemsSource = await Task.Run(() => { var T = new List(); for (int i = 0; i < 10000; i++) { T.Add(i.ToString()); } return T; }); }` Зачем ожидается то что не возвращает результат?

    –  Jul 07 '17 at 19:29
  • @Birdy: У меня окно не зависает, что я делаю не так? Для примера заменил 10000 на 1000000, во время работы Task.Run окно можно вполне таскать по экрану. А вот присвоение ItemsSource и создание огромного количества item'ов, да, подтормаживает, но ТС спрашивает не об этом. Попробуйте для тестов добавить Thread.Sleep на каждой итерации. – VladD Jul 07 '17 at 19:34
  • @Birdy - не зависает. модификатор async в т.ч. говорит компилятору, что данный метод нужно специальным образом скомпилировать. Побочным эффектом специальной компиляции и является тот факт, что вместо Task можно возвращать void, а вместо Task<T> -- T. – user270576 Jul 07 '17 at 20:27
1

Async делит ваш метод надвое, и работает так:

  • исполняет весь код, предшествующий ожиданию await
  • завершает исходный метод (выходит)
  • Ждет, пока SynchronizationContext не сообщит ему, что awaited код завершен
  • исполняет весь код, следующий за await

Проблема в том, что в Async() нет await, поэтому все работает синхронно.

Если ваш метод async не зависит от других методов async (выполняет работу с CPU), вы должны использоватьTask.Run:

public async Task Async()
{
    tt.ItemsSource = await Task.Run(() =>
    {
      var T = new List<string>();
      for (int i = 0; i < 10000; i++)
      {
           T.Add(i.ToString());
      }
      return T;
    });
}

async/await how-to

async/await intro

как работает

Очень подробно

UPDATE

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

  • 2
    Нельзя делать присваивание tt.ItemsSource = T; в другом потоке – Pavel Mayorov Jul 07 '17 at 17:38
  • такая же проблема как в ответе ниже – Саске Jul 07 '17 at 17:43
  • @PavelMayorov Да, вы правы. Ошибка копирования - alexsandr-ter правильно написал – user270576 Jul 07 '17 at 17:43
  • @Санитариум - Покажите вращающийся курсор мыши и отключите все кнопки – user270576 Jul 07 '17 at 17:44
  • @PavelMayorov, он исправил. – Qwertiy Jul 07 '17 at 17:45
  • скурсором понятно ну а если это будет долго идти я хочу например что то другое сделать с окном например переместить его.Я его даже переместить не могу оно польностью заблокировано – Саске Jul 07 '17 at 17:53
  • @Санитариум - курсор мыши. Или вы можете создать всплывающее сообщение с надписью «Двигаем спутники на позицию» – user270576 Jul 07 '17 at 18:00
  • @Санитариум - я написал решение - tt.ItemsSource = await Task.Run(() =>... Позволит вам перемещать окно – user270576 Jul 07 '17 at 18:02
1

Используйте Dispatcher, если не доступны методы Invoke и другие, допишите Dispatcher.CurrentDispatcher.необходимый_метод

Dispatcher.Invoke(() => // или InvokeAsync
{
    // Ваш код здесь
}, DispatcherPriority.Background);

Так же можно запустить поток, в этом же диспетчере.

Dispatcher.Invoke(DispatcherPriority.Background, (ThreadStart) delegate
{
    /*Ваш код здесь*/ 
});

Думаю это то что вам нужно:

Dispatcher.Invoke(async () =>
{
    await Task.Run(async () =>
    {
        await Task.Delay(new TimeSpan(0, 0, 10)); // имитируем долгую операцию
    });
    Title = "Выполнено!";
}, DispatcherPriority.Background);

Если все же не понятно:

Dispatcher.Invoke(async () =>
{
    tt.ItemsSource = await Task.Run(async () =>
    {
        List<string> result = new List<string>();
        await Task.Delay(new TimeSpan(0, 0, 5)); // имитируем долгую операцию
        for (int i = 0; i < 10; i++)
        {
            result.Add($"Item ID: {i}");
        }
        return result;
    });
    Title = "Выполнено!";
}, DispatcherPriority.Background);
  • Не забудьте сделать метод Window_Loaded синхронным. –  Jul 07 '17 at 19:00