何时避免异步等待

时间:2019-10-02 03:10:54

标签: c# asynchronous async-await stack overflow

异步/等待是编写响应式GUI的好方法,但正如我想的那样,这里的奶酪不是免费的。我认为实现Await语句的唯一方法是内存堆栈。每次调用Await时,都会在内存堆栈中放一个新的指针。因此,如果调用频繁的Await并导致结果延迟,则会导致堆栈溢出(即您无法在此处搜索的关键字:))

让我们假设我们周期性地异步检查某些连接状态。 使用Form1创建一个简单的Windows窗体项目,并且窗体代码在这里。

using System;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace testasync
{
    public partial class Form1 : Form
    {
        int CallCount = 0;
        int CalBackCount = 0;
        Random r = new Random();

        private Button butStart = new Button();
        private Button butStop = new Button();
        private Timer timer1 = new Timer();
        private Label labCalls =new Label();
        private Label labReturns = new Label();

        public Form1()
        {
            this.ClientSize = new System.Drawing.Size(450, 200);

            butStart.Location = new System.Drawing.Point(51, 38);
            butStart.Size = new System.Drawing.Size(139, 47);
            butStart.Text = "Start";
            butStart.Click += new System.EventHandler(this.butStart_Click);

            butStop.Location = new System.Drawing.Point(237, 38);
            butStop.Size = new System.Drawing.Size(139, 47);
            butStop.Text = "Stop";
            butStop.Click += new System.EventHandler(this.butStop_Click);

            labCalls.Location = new System.Drawing.Point(48, 149);
            labCalls.Size = new System.Drawing.Size(100, 23);

            labReturns.Location = new System.Drawing.Point(237, 149);
            labReturns.Size = new System.Drawing.Size(100, 23);

            Controls.Add(this.labCalls);
            Controls.Add(this.labReturns);
            Controls.Add(this.butStart);
            Controls.Add(this.butStop);


            timer1.Interval = 5;
            timer1.Tick += new System.EventHandler(this.timer1_Tick);
        }

        private void butStart_Click(object sender, EventArgs e)
        {
            timer1.Enabled = true;
        }

        private void butStop_Click(object sender, EventArgs e)
        {
            timer1.Enabled = false;
        }


        private async Task myWork()
        {
            CallCount++;
            await Task.Delay(2000);
            if (r.Next(100)>10) //Check Connection
                await Task.Delay(100000000);
            CalBackCount++;
        }

        private async void timer1_Tick(object sender, EventArgs e)
        {
            labCalls.Text = CallCount.ToString();
            await myWork();
            labReturns.Text = CalBackCount.ToString();
        }
    }
}

我使用随机检查来等待很长时间以等待连接响应,而不是检查连接。

如果我们运行项目并单击开始按钮,我们将看到应用程序的内存消耗随时间增加。因此,我认为这不是使用Await语句的正确方法,我认为更好的方法是使用带有委托的线程或带有进度事件的漂亮组件BackgroundWorker。还是有更好的解决方案?

1 个答案:

答案 0 :(得分:1)

您对async-await的工作方式的假设不正确。

每个异步方法都将在状态机中转换,该状态机将方法分为几种方法(开始,等待和结束之间的部分)并协调其执行。

流氓异步方法比堆栈更可能耗尽堆。