Как использовать async / await с бесконечностью цикла? - PullRequest
0 голосов
/ 24 марта 2019

Я хочу зациклить бесконечность, чтобы все время проверять n = 0 или n = 1

    public int check()
    {

        int n;

        Int32 nTest = 0;
        nTest = RID.Read(obj);

        if (nTest != 0)
        {
            n = 0;
        }
        else
        {
            n = 1;
        }

        return n;
    }

    private async void Form1_Load(object sender, EventArgs e)
    {
        Task<int> task = new Task<int>(check);
        task.Start();

        while (true)
        {
            int c = await task;

            label7.Text = c.ToString();
        }
    }

Я пытаюсь, пока true в Form1_Load для начала, проверить значение n. когда я запускаю программу, она зависает и не может ничего щелкнуть. Как это исправить?

Ответы [ 3 ]

0 голосов
/ 27 марта 2019

Вы должны использовать Microsoft Reactive Framework (он же Rx) - NuGet System.Reactive.Windows.FOrms и добавить using System.Reactive.Linq; - тогда вы можете сделать это:

private async void Form1_Load(object sender, EventArgs e)
{
    IDisposable subscription =
        Observable
            .While(() => true, Observable.Start(() => RID.Read(obj) == 0 ? 1 : ))
            .ObserveOn(this)
            .Subscribe(c => label7.Text = c.ToString());
}

Вот и весь ваш код.

0 голосов
/ 02 апреля 2019

при запуске программы она зависает и не может ничего щелкнуть.

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

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

private async void Form1_Load(object sender, EventArgs e)
{
  while (true)
  {
    int c = await Task.Run(() => check());
    label7.Text = c.ToString();
  }
}

В качестве альтернативы, если вы хотите использовать бесконечный цикл в фоновом коде, вы можете использовать IProgress<T>, чтобы сообщить о прогрессепоток пользовательского интерфейса:

private Task CheckForever(IProgress<int> progress)
{
  while (true)
  {
    var c = check();
    progress?.Report(c);
  }
}

private async void Form1_Load(object sender, EventArgs e)
{
  var progress = new Progress<int>(c => label7.Text = c.ToString());
  await CheckForever(progress);
}

Обратите внимание, что код Task.Run всегда чище, проще в обслуживании и более безопасен, чем код BackgroundWorker.

0 голосов
/ 26 марта 2019

Async-await предназначен для того, чтобы ваша программа не ожидала, пока другой процесс завершит свою работу в течение относительно короткого времени.Подумайте об ожидании результатов запроса к базе данных, чтении файла, получении данных из базы данных и т. Д.

Если вы используете async-await, ваш поток не будет бездействовать, пока другая обработка выполняет запрос.Вместо этого if может оглянуться и посмотреть, есть ли другие дела.

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

Если вы хотите запустить длительный процесс, подумайте о секундах, может быть, минуты или даже больше, необходимого временизапуск этого отдельного потока больше не важен.

Так что в вашем случае я бы посоветовал использовать BackgroundWorker

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

. В вашем случае фоновому работнику нужна только небольшая функция, поэтому для подписки на событие DoWork будет достаточно.

Используйте набор инструментов Visual Studio для добавления фонового редактора или добавьте его вручную в конструкторе:

// Constructor:
public Form1()
{
    InitializeComponent();

    // Create a backgroundworker
    this.backgroundWorker = new BackgroundWorker
    {
        // only if you want to display something during processing:
        WorkerReportsProgress = true,

        WorkerSupportsCancellation = true; // to  neatly close your form 
    };

    this.backgroundWorker.DoWork += this.BackgroundProcedure;
    this.backgroundWorker.ProgressChanged += this.BackgroundProcessReport;
    this.backgroundWorker.RunworkerCompleted += this.BackgroundWorkerFinished;
}

Запуск и остановка просты:

bool IsBackGroundWorkerBusy => this.backgroundWorker.IsBusy;
void StartBackgroundWork()
{
    if (this.IsBackGroundWorkerbusy) return; // already started;

    this.DisplayBackgroundWorkerActive(); // for example, show an ajax loader 
    this.backgroundWorker.RunworkerAsync();

    // or if you want to start with parameters:
    MyParameters backgroundWorkerParameters = new MyParameters(...);
    this.backgroundWorker.RunworkerAsync(backgroundWorkerParameters);

}
void RequestCancelBackgroundWork()
{
    this.DisplayBackgroundWorkerStopping();
    this.backgroundWorker.CancelAsync();
}

Фоновая процедура .Это выполняется фоновым потоком.Вы не можете вызвать любую функцию, связанную с пользовательским интерфейсом в этой процедуре.Если вы хотите обновить что-либо в пользовательском интерфейсе, используйте ReportProgress.

void BackGroundProcedure(object sender, DoworkEventArgs e)
{
    // if you know that the backgroundworker is started with parameters:
    MyParameters parameters = (MyParameters)e.Argument;

    // do you work, regularly check if cancellation is requested:
    while (!e.Cancel)
    {
        ...

        // only if progress reports are needed: report some progress, not too often!
        MyProgressParams progressParams = new MyProgressParams(...);
        this.ReportProgress(..., progressParams);
    }

    // if here, the thread is requested to cancel
    // if needed report some result:
    e.Result = ...;
}

Progress Report .Он выполняется вашим потоком пользовательского интерфейса, поэтому при желании вы можете обновить элементы пользовательского интерфейса.Это основная функциональность этого метода.

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

void BackgroundProcessReport(object sender, ProgressChangedEventArgs e)
{
     // the background worker reported some progress.
     ... // update UI
}

Runworker Completed вызывается после завершения потока.Содержит данные, присвоенные e.Result.Он выполняется потоком пользовательского интерфейса, поэтому вы можете делать любые связанные с пользовательским интерфейсом вещи, которые вам нужны:

void BackgroundWorkerFinished(object sender, RunWorkerCompletedEventArgs e)
{
    this.DisplayBackgroundWorkerFinished(); // for example: hide ajax loader

    ... // use e to process result
}

Аккуратное закрытие вашей формы

Если ваша форма закрывается, фоновый работник должензакончить до того, как окно можно будет утилизировать.Правильный метод использует событие OnClosing:

bool closeFormRequested = false;

void OnFormClosing(object sender, CancelEventArgs e)
{
     // if background worker busy: request cancellation; can't close the form right now
     if (this.IsBackgroundworkerBusy)
     {
         this.closeFormRequested = true;
         e.Cancel = true;
         this.RequestCancelBackgroundWork();
     }
     else
     {   // background worker not busy, OK to close
         e.Cancel = false;
     }
}

Через некоторое время фоновый работник сообщает, что оно завершено:

void BackgroundWorkerFinished(object sender, RunWorkerCompletedEventArgs e)
{
    this.DisplayBackgroundWorkerFinished(); 
    ... // process result

   // if requested: close the form:
   if (this.closeFormRequested)
       this.Close();
}

this.Close() приведет к OnFormClosing, но этоесли фоновый работник не будет занят, и закрытие будет продолжено

Наконец: фоновый работник реализует IDisposable, не забудьте удалить его, когда ваша форма будет удалена.

...