Sni.DelWoods Ответов: 3

C#/WPF: запуск метода parallel


У меня есть пользовательский элемент управления в главном окне с кнопкой (Button1).
Событие Button1_click начинает извлекать данные из веб-сервиса (выполняется >1 час).

Чтобы сократить время, нужно выполнить 10 задач для извлечения данных.

В моем примере метод DoWork() запускается не сразу.
Это начинается после событие Button1_click выполнено.
Таким образом, процесс post не имеет данных.

Как я могу реализовать функцию ожидания, чтобы дождаться завершения всех потоков?

Что я уже пробовал:

элемент управления UserControl:
List<string> fetchedData = new List<string>();

private void Button1_Click(object sender, System.Windows.RoutedEventArgs e)
{
    fetchedData.Clear();
    FetchData();

    // problem: 
    // (pseudo code) wait until all data is fetched
    while(true) { break; }

    // Post process
    foreach (var item in fetchedData) {
      // do something with the data (complete data list!)
    }
}

private void FetchData() 
{
  var threads = new Thread[10];

  for (int segment = 0; segment <= 9; segment++)
  {
    var worker = new ThreadStart(
        ()=>this.Dispatcher.BeginInvoke(
            DispatcherPriority.Normal,
            (ThreadStart)delegate ()
            {
                DoWork(iSegment); //executed AFTER exiting the button1_click event
            }
        )
    );

    threads[segment] = new Thread(worker);
    threads[segment].Start();                     
  }
}

private void DoWork (int segment) {
   fetchedData.Add( Helpers.GetWebserviceData(segment) );

   // output some data to the mainWindow to see the actual process
   Helpers.mainWindow.LabelStatus.Content = "Current segment: " + i.ToString();
}

3 Ответов

Рейтинг:
13

Vincent Maverick Durano

У Стивена Клири тоже есть отличные статьи об асинхронном программировании в .NET: Async и await[^]


Рейтинг:
12

Sni.DelWoods

Спасибо за ваши комментарии. Я построил простой пример, который вызывает некоторые методы с задержками. Выход, как и ожидалось, находится в правильном порядке (из-за различных задержек).

Моя проблема сейчас:
У меня есть веб-сервис во внешнем статическом классе (см. ниже).
Эта оболочка не имеет ключевого слова async, и в клиенте webservice нет асинхронного вызова (webreference to external service).

Поможет ли это поместить оболочку в экземпляр класса так, чтобы каждая задача вызывала собственный экземпляр оболочки?

public static class Wrapper {
   public static string CallWebservice(string param) {
      var client = new Webservice.Client();
      // takes up to 3 seconds per call
      return client.Method(param);
   }
}

using System;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Threading;

namespace AsyncTest
{
    public static class Helpers
    {
        public static MainWindow mainWindow = null;
    }

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            Helpers.mainWindow = this;
        }

        public static void AddLog(string text)
        {
            Helpers.mainWindow.Dispatcher.Invoke(
                () => Helpers.mainWindow.LblOutput.Content += DateTime.Now.ToString() + $" [{text}]\r\n",
                DispatcherPriority.Background
            );
        }

        private async void ButtonDoAsync_Click(object sender, RoutedEventArgs e)
        {
            AddLog("Button Click:Start");

            StartAsync();

            for (var i = 0; i < 5; i++)
            {
                AddLog($"Button Click:Wait {i}");
                await Task.Delay(500);
            }

            AddLog("Button Click:End");
        }

        static async void StartAsync()
        {
            var tasks = new Func<Task>[]
                {
                    () => DoSomethingAsync("t1",800),
                    () => DoSomethingAsync("t2",350),
                    () => DoSomethingAsync("t3",450)
                };

            await Task.WhenAll(tasks.Select(task => task()).ToArray());
        }

        static async Task DoSomethingAsync(string taskId, int delay)
        {
            for (var i = 0; i < 5; i++)
            {
                AddLog(Wrapper.CallWebservice(taskID));  // no async here
                AddLog($"Async {taskId} {i}");
                await Task.Delay(delay);
            }
        }
    }
}