C# 异步 Foreach

Posted

技术标签:

【中文标题】C# 异步 Foreach【英文标题】:C# Async Foreach 【发布时间】:2022-01-12 13:45:00 【问题描述】:

我正忙于在 Windows 窗体中开发一个股票跟踪器应用程序以备不时之需。为了优化我的代码,我决定重写使所有内容保持最新的 main 函数。我认为选择正确的行并从那里工作会更容易,而不是为正确的控件搜索所有控件。问题是:它不断跳过我要运行的任务,并且不再更新它。我已经尝试了很多我在互联网上找到的东西,但我无法让它工作。所以我恢复到最简单的原始代码形式,看看你们是否有想法。以下是这一切的精髓:

        public async void KeepUpdatingEverything(List<object> positionInfo, List<string> tickerList)
        
            foreach (string ticker in tickerList)
            
                //code that gets the right row
                List<object> priceInfo = await GetStockPrices(ticker));
                //code that updates all the labels
            
        

这个想法是,当调用 KeepUpdating 函数时,它会检查带有代码的列表,获取每个代码的价格并随后更新所有相关标签。但我似乎无法让它工作,因为它一直跳过异步调用。有什么想法吗?

在输入第一个代码时,会调用一次 KeepUpdatingEverything,之后它只会不断更新代码列表。

        private async void button1_Click(object sender, EventArgs e)
        
            string ticker;
            using (Prompt prompt = new Prompt("Enter the ticker symbol", "Add ticker"))
            
                ticker = prompt.Result;
                ticker = ticker.ToUpper();
                if (!string.IsNullOrEmpty(ticker))
                

                    using (Prompt prompt2 = new Prompt("Enter your volume", "Add ticker"))
                    
                        if (Int32.TryParse(prompt2.Result, out int volume) == true)
                        
                            using (Prompt prompt3 = new Prompt("Enter your buy price", "Add ticker"))
                            
                                if (Double.TryParse(prompt3.Result, out double buyPrice) == true)
                                
                                    try
                                    
                                        List<object> priceInfo = await GetStockPrices(ticker);
                                        FillTickerLabel(ticker);

                                        List<object> positionInfo = GetPositionVars(ticker, volume, buyPrice, Convert.ToDouble(priceInfo[1]));
                                        FillPositionLabel(ticker, priceInfo[0].ToString(), positionInfo);

                                        List<object> changeInfo = GetChangeVars(ticker, priceInfo);
                                        FillChangeLabels(ticker, priceInfo[0].ToString(), changeInfo);

                                        List<string> tickerList = new List<string>();
                                        tickerList.Add(ticker);
                                        if (tickerList.Count <= 1)
                                        
                                            _cancellationToken = new CancellationTokenSource();
                                            _runningTask = StartTimer(() => KeepUpdatingEverything(positionInfo, tickerList), _cancellationToken);

                                        
                                    
                                    catch
                                    
                                        MessageBox.Show("Ticker does not exist, or entered incorrect value somewhere else");
                                    
                                
                                else
                                
                                    MessageBox.Show("You did not enter one of the textboxes correctly");
                                
                            
                        
                        else
                        
                            MessageBox.Show("You did not enter one of the textboxes correctly");
                        
                    
                
                else
                
                    MessageBox.Show("You did not enter one of the textboxes correctly");
                
            
        

最后是StartTimer函数:

        private async Task StartTimer(Action action, CancellationTokenSource cancellationTokenSource)
        
            try
            
                while (!cancellationTokenSource.IsCancellationRequested)
                
                    await Task.Delay(5000, cancellationTokenSource.Token);
                    action();
                
            
            catch (OperationCanceledException)  
        

【问题讨论】:

KeepUpdatingEverything 方法在哪里/如何被调用?你也知道这篇文章吗? Avoid async void. _runningTask = StartTimer(() =&gt; KeepUpdatingEverything... StartTimer 方法在做什么? @TheodorZoulias 启动计时器以每 5 秒运行一次 KeepUpdatingEverything。我检查了这篇文章,但将其更改为 Task 类型并没有任何区别。我将在主帖中编辑完整的 StartTimer 代码 “它一直跳过我要运行的任务,并且不再更新它。” 你知道await GetStockPrices(ticker)是否抛出异常?在这种情况下,我的期望是异常会升级为应用程序崩溃。除非存在一些额外的错误吞噬代码(空 catch 块),否则您已从问题中省略。 【参考方案1】:

async void 表示你不返回调用者可以await 的任务。

你应该像这样使用async Task

public async Task KeepUpdatingEverything(List<object> positionInfo, List<string> tickerList)

【讨论】:

嗯,我试过了,但似乎没有什么不同。它仍然取消执行任务。【参考方案2】:

更新汉斯的答案。 你需要await父方法中的调用。

await KeepUpdatingEverything(...)

它会一直等到完成。

顺便说一句,在 foreach 中 await 并不是最好的方法。 将所有任务结果放入列表或数组中并await它们会更容易。

await Tasks.WhenAll(tasks);

【讨论】:

试过了,但遗憾的是似乎也没有什么不同

以上是关于C# 异步 Foreach的主要内容,如果未能解决你的问题,请参考以下文章

C# :异步编程的注意点

C#异步编程一

[C#] 异步编程 - 剖析异步方法

C#的异步回调函数

C#异步执行代码块

C#:异步NamedPipeServerStream理解