简体   繁体   English

如何在无限循环中使用异步/等待?

[英]How to use async/await with loop infinity?

I want to loop infinity to check all time n=0 or n=1我想循环无穷大来检查所有时间 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();
        }
    }

I try to while true in Form1_Load for start check n value.我尝试在 Form1_Load 中为 true 以开始检查 n 值。 when I run program it will freezes and can not click anything.当我运行程序时,它会冻结并且无法单击任何内容。 How to fix it ?如何解决?

Async-await is meant to stop your program from waiting idly for another process to finish during a relatively short time. Async-await 旨在阻止您的程序在相对较短的时间内无所事事地等待另一个进程完成。 Think about waiting for the results of a database query, reading a file, fetching data from a database etc.考虑等待数据库查询的结果、读取文件、从数据库中获取数据等。

If you use async-await your thread won't wait idly while the other processing is doing the request.如果您使用 async-await,您的线程不会在其他处理正在执行请求时空闲等待。 Instead if can look around to see if there are other things to do.相反,如果可以环顾四周,看看是否还有其他事情要做。

Of course you could accomplish the same effect by creating a separate thread to do the waiting for you.当然,您可以通过创建一个单独的线程来等待您来实现相同的效果。 Apart from that this will make your code more complicated, creating a separate thread is fairly time consuming.除此之外,这会使您的代码更加复杂,创建单独的线程相当耗时。

If you want to start a long-running process, think of seconds, maybe minutes or even longer, the time needed to start this separate thread is not important anymore.如果你想启动一个长时间运行的进程,想想几秒钟,也许几分钟甚至更长时间,启动这个单独的线程所需的时间不再重要。

So in your case, I'd advise to use a BackgroundWorker所以在你的情况下,我建议使用BackgroundWorker

If your background worker needs a lot of procedures to do its work, consider deriving from BackgroundWorker and start working in an override of BackgroundWorker.OnDoWork .如果您的后台工作人员需要大量过程来完成其工作,请考虑从 BackgroundWorker 派生并开始在BackgroundWorker.OnDoWork的覆盖中工作。

In your case the backgroundworker needs only a small function, so it will be enough to subscribe to the DoWork event.在您的情况下,后台工作人员只需要一个小功能,因此订阅DoWork事件就足够了。

Use your visual studio toolbox to add a backgroundworker, or add it manually in your constructor:使用您的 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;
}

Starting and stopping is easy:启动和停止很容易:

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();
}

Background procedure .后台程序 It is executed by the background thread.它由后台线程执行。 You can't call any UI related function in this procedure.您不能在此过程中调用任何与 UI 相关的函数。 If you want to update anything in the UI, use ReportProgress.如果要更新 UI 中的任何内容,请使用 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 .进度报告 It is executed by your UI thread, so if desired you can update UI elements.它由您的 UI 线程执行,因此如果需要,您可以更新 UI 元素。 This is the major functionality of this method.这是此方法的主要功能。

The first parameter in report progress is a number, usually from 0..100, used by receivers of the progress event to update some visual display about progress.报告进度中的第一个参数是一个数字,通常从 0..100 开始,进度事件的接收者使用它来更新有关进度的一些视觉显示。 If you don't have any indication how long the progress needs, don't use the value.如果您没有任何迹象表明进度需要多长时间,请不要使用该值。 The progressParams can be any object. progressParams 可以是任何对象。

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

Runworker Completed is called when the thread finishes. Runworker Completed在线程完成时被调用。 It contains the data assigned to e.Result.它包含分配给 e.Result 的数据。 It is executed by the UI thread, so you can do any UI related stuff that you want:它由 UI 线程执行,所以你可以做任何你想做的 UI 相关的事情:

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

    ... // use e to process result
}

Neat Closure of your form表单的整洁关闭

If your form is closing, the backgroundworker has to be finished before the window can be disposed.如果您的表单正在关闭,则必须在处理窗口之前完成后台工作。 A proper method is using the OnClosing event:一个正确的方法是使用 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;
     }
}

After a while the backgroundworker reports that it is finished:过了一会儿,后台工作人员报告说它完成了:

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

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

this.Close() will lead to OnFormClosing , but this time the background worker will not be busy, and closing will continue this.Close()会导致OnFormClosing ,但是这个时候后台worker不会很忙,关闭会继续

Finally: a background worker implements IDisposable, don't forget to Dispose it when your form is Disposed.最后:后台工作人员实现了 IDisposable,不要忘记在你的表单被处置时处置它。

You should use Microsoft's Reactive Framework (aka Rx) - NuGet System.Reactive.Windows.FOrms and add using System.Reactive.Linq;您应该使用 Microsoft 的 Reactive Framework(又名 Rx)- NuGet System.Reactive.Windows.FOrmsusing System.Reactive.Linq;添加using System.Reactive.Linq; - then you can do this: - 那么你可以这样做:

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());
}

That's your entire code.这就是你的全部代码。

when I run program it will freezes and can not click anything.当我运行程序时,它会冻结并且无法单击任何内容。

This is because the task is only started once.这是因为任务只启动一次。 After it completes, it stays completed, so the first await asynchronously waits for the task to complete, but all the other await s just return that first value, immediately and synchronously.完成后,它保持完成状态,因此第一个await异步等待任务完成,但所有其他await只是立即且同步地返回第一个值。 This is why you can't click anything: you end up with a synchronous infinite loop on your UI thread.这就是你不能点击任何东西的原因:你最终在你的 UI 线程上出现了一个同步的无限循环。

To fix this, I recommend using Task.Run to start the background task.为了解决这个问题,我建议使用Task.Run来启动后台任务。 Task.Run is far less error-prone than the task constructor and Start . Task.Run比任务构造函数和Start更不容易出错。

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

Alternatively, if you wanted the infinite loop in your background code, you could use IProgress<T> to report progress to the UI thread:或者,如果您希望后台代码中出现无限循环,您可以使用IProgress<T>向 UI 线程报告进度:

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);
}

Note that Task.Run code is always cleaner, easier to maintain, and more type-safe than BackgroundWorker code.请注意, Task.Run代码始终比BackgroundWorker代码更干净、更易于维护且类型更安全。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM