[英]Dotnet yield with IEnumerable<Task<int>> not working
玩弄 yield 和 Task。
以下簡單示例運行良好。
class Program
{
private static void Main(string[] args)
{
string[] messages = { "First task", "Second task", "Third task", "Fourth task" };
var taskList = CreateTaskList(messages).ToList();
taskList.ForEach(task => task.Start());
Task.WaitAll(taskList.ToArray());
Console.WriteLine("Main method complete. Press enter to finish.");
Console.ReadLine();
}
static IEnumerable<Task> CreateTaskList(string[] messages)
{
foreach (var message in messages)
{
yield return new Task(obj => PrintMessage((string)obj!), message);
}
}
static void PrintMessage(object message)
{
Console.WriteLine("Message: {0}", message);
}
}
但是下面沒有。 是否存在一些僵局? 它停留在 Task.WaitAll。 所以我從控制台得到的所有東西都是Before wait all
class SimpleClass
{
public int Counter { get; set; }
}
class Program
{
private static void Main(string[] args)
{
// create the simple object
var simpleObject = new SimpleClass();
var taskList = CreateTaskEnumerable(simpleObject, 10);
// Start all of the tasks
foreach (var task in taskList)
task.Start();
Console.WriteLine("Before wait all");
// wait for all of the tasks to complete
Task.WaitAll(taskList.ToArray());
Console.WriteLine("After wait all");
foreach (var task in taskList)
simpleObject.Counter += task.Result;
// write out the counter value
Console.WriteLine("Expected value {0}, Counter value: {1}", 10000, simpleObject.Counter);
// wait for input before exiting
Console.WriteLine("Press enter to finish");
Console.ReadLine();
}
private static IEnumerable<Task<int>> CreateTaskEnumerable(SimpleClass simpleObject, int numberOfTasks)
{
for (int i = 0; i < numberOfTasks; i++)
{
yield return new Task<int>((stateObject) =>
{
// get the state object
var localCounter = (int)stateObject!;
// enter a loop for 1000 increments
for (int j = 0; j < 1000; j++)
// increment the counters
localCounter++;
return localCounter;
}, simpleObject.Counter);
}
}
}
如果我完全刪除 yield,上面的內容將如下所示,並且有效。 它給出 output 如下。 我也期待上面的相同 output,但那是卡住了。 為什么?
Before wait all
After wait all
Expected value 10000, Counter value: 10000
Press enter to finish
該程序。
class SimpleClass
{
public int Counter { get; set; }
}
class Program
{
private static void Main(string[] args)
{
// create the bank account instance
var simpleObject = new SimpleClass();
// create an list of tasks
var taskList = new List<Task<int>>();
for (int i = 0; i < 10; i++)
{
// create a new task
var task = new Task<int>((stateObject) =>
{
// get the state object
var localCounter = (int)stateObject!;
// enter a loop for 1000 increments
for (int j = 0; j < 1000; j++)
// increment the counters
localCounter++;
return localCounter;
}, simpleObject.Counter);
taskList.Add(task);
}
// Start all of the tasks
foreach (var task in taskList)
task.Start();
Console.WriteLine("Before wait all");
// wait for all of the tasks to complete
Task.WaitAll(taskList.ToArray());
Console.WriteLine("After wait all");
foreach (var task in taskList)
simpleObject.Counter += task.Result;
// write out the counter value
Console.WriteLine("Expected value {0}, Counter value: {1}", 10000, simpleObject.Counter);
// wait for input before exiting
Console.WriteLine("Press enter to finish");
Console.ReadLine();
}
}
在第一個示例中,您正在調用CreateTaskList(messages).ToList()
,這會強制CreateTaskList
在繼續之前產生所有任務。 在第二個示例中,您沒有調用ToList()
,任務在foreach
中產生,然后開始。 問題在Task.WaitAll(taskList.ToArray());
. 它需要IEnumerable
並再次產生任務,您正在等待它們完成,但它們尚未開始。 換句話說,每次您在“已產生”的IEnumerable
上調用foreach
或ToList()
時,它都會運行方法CreateTaskEnumerable
並創建新任務。
一種解決方案是調用var taskList = CreateTaskEnumerable(simpleObject, 10).ToList()
或者您可以在CreateTaskEnumerable
中手動創建列表並返回它。
PS 我建議您閱讀yield return
的工作原理,或在https://sharplab.io/中對其進行測試。 它基本上創建了IEnumerable
從您的方法中獲取數據。 這意味着每次枚舉IEnumerable
時都會執行您的方法。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.