C# 如何跟踪一个循环中完成了多少异步任务?
我有一些代码,可以循环浏览记录列表,为每个记录启动导出任务,并在每次任务完成时将进度计数器增加1,这样用户就可以知道整个过程有多远 但是,根据循环的计时,我经常看到输出显示一个较高的数字,然后是一个较低的数字 例如,我希望看到如下输出: Exporting A Exporting B Exporting C Exporting D Exporting E Finished 1 / 5 Finished 2 / 5 Finished 3 / 5 Finished 4 / 5 Finished 5 / 5C# 如何跟踪一个循环中完成了多少异步任务?,c#,multithreading,c#-4.0,task-parallel-library,task,C#,Multithreading,C# 4.0,Task Parallel Library,Task,我有一些代码,可以循环浏览记录列表,为每个记录启动导出任务,并在每次任务完成时将进度计数器增加1,这样用户就可以知道整个过程有多远 但是,根据循环的计时,我经常看到输出显示一个较高的数字,然后是一个较低的数字 例如,我希望看到如下输出: Exporting A Exporting B Exporting C Exporting D Exporting E Finished 1 / 5 Finished 2 / 5 Finished 3 / 5 Finished 4 / 5 Finished 5
var tasks = new List<Task>();
int counter = 0;
StatusMessage = string.Format("Exporting 0 / {0}", count);
foreach (var value in myValues)
{
var valueParam = value;
// Create async task, start it, and store the task in a list
// so we can wait for all tasks to finish at the end
tasks.Add(
Task.Factory.StartNew(() =>
{
Debug.WriteLine("Exporting " + valueParam );
System.Threading.Thread.Sleep(500);
counter++;
StatusMessage = string.Format("Exporting {0} / {1}", counter, count);
Debug.WriteLine("Finished " + counter.ToString());
})
);
}
// Begin async task to wait for all tasks to finish and update output
Task.Factory.StartNew(() =>
{
Task.WaitAll(tasks.ToArray());
StatusMessage = "Finished";
});
导出
出口B
出口C
出口D
出口电子产品
完成1/5
完成2/5
完成3/5
完成4/5
完成5/5
但我得到的是这样的输出
Exporting A
Exporting B
Exporting C
Exporting D
Exporting E
Finished 1 / 5
Finished 2 / 5
Finished 5 / 5
Finished 4 / 5
Finished 3 / 5
导出
出口B
出口C
出口D
出口电子产品
完成1/5
完成2/5
完成5/5
完成4/5
完成3/5
我不希望输出是精确的,因为我在更新/使用它时没有锁定值(有时它会输出相同的数字两次,或者跳过一个数字),但是我不希望它返回
我的测试数据集是72个值,相关代码如下所示:
Exporting A
Exporting B
Exporting C
Exporting D
Exporting E
Finished 1 / 5
Finished 2 / 5
Finished 3 / 5
Finished 4 / 5
Finished 5 / 5
var tasks = new List<Task>();
int counter = 0;
StatusMessage = string.Format("Exporting 0 / {0}", count);
foreach (var value in myValues)
{
var valueParam = value;
// Create async task, start it, and store the task in a list
// so we can wait for all tasks to finish at the end
tasks.Add(
Task.Factory.StartNew(() =>
{
Debug.WriteLine("Exporting " + valueParam );
System.Threading.Thread.Sleep(500);
counter++;
StatusMessage = string.Format("Exporting {0} / {1}", counter, count);
Debug.WriteLine("Finished " + counter.ToString());
})
);
}
// Begin async task to wait for all tasks to finish and update output
Task.Factory.StartNew(() =>
{
Task.WaitAll(tasks.ToArray());
StatusMessage = "Finished";
});
var tasks=newlist();
int计数器=0;
StatusMessage=string.Format(“导出0/{0}”,计数);
foreach(myValues中的var值)
{
var valueParam=价值;
//创建异步任务,启动它,并将任务存储在列表中
//因此,我们可以等待所有任务在最后完成
任务。添加(
Task.Factory.StartNew(()=>
{
Debug.WriteLine(“导出”+valueParam);
系统.线程.线程.睡眠(500);
计数器++;
StatusMessage=string.Format(“导出{0}/{1}”,计数器,计数);
Debug.WriteLine(“Finished”+counter.ToString());
})
);
}
//开始异步任务以等待所有任务完成并更新输出
Task.Factory.StartNew(()=>
{
Task.WaitAll(tasks.ToArray());
StatusMessage=“完成”;
});
输出可以在调试语句和StatusMessage
输出中向后显示
什么是正确的方法来统计一个循环中完成了多少异步任务,以避免出现此问题?在本示例中,
计数器
变量表示多个线程之间的共享状态。在共享状态下使用++
运算符是不安全的,并且会产生错误的结果。它基本上归结为以下说明
++
Interlocked.Increment(ref counter);
此操作专门用于更新可能在多个线程之间共享的状态。联锁将发生原子,不会受到我概述的比赛条件的影响
即使在我建议的修复之后,值的实际无序显示也会遇到类似的问题。增量和显示操作不是原子的,因此一个线程可以在增量和显示之间中断另一个线程。如果希望操作不被其他线程中断,则需要使用锁
object lockTarget = new object();
int counter = 0;
...
lock (lockTarget) {
counter++;
StatusMessage = string.Format("Exporting {0} / {1}", counter, count);
Debug.WriteLine("Finished " + counter.ToString());
}
请注意,由于
计数器的增量现在发生在锁内部,因此不再需要使用联锁。增量会得到混合输出,因为计数器的增量顺序与调试的顺序不同。执行WriteLine(…)
方法
要获得一致的进度报告,可以在任务中引入报告锁
tasks.Add(
Task.Factory.StartNew(() =>
{
Debug.WriteLine("Exporting " + valueParam );
System.Threading.Thread.Sleep(500);
lock(progressReportLock)
{
counter++;
StatusMessage = string.Format("Exporting {0} / {1}", counter, count);
Debug.WriteLine("Finished " + counter.ToString());
}
})
);
你确定异步
过程会根据我看到的情况以升序
输出吗?我认为不会,除非你在列表
上执行排序
,因为你在基本相同的时间启动所有任务,它们将以随机顺序完成。即使使用Interlocked.Increment“Finished n/5”也不会是单调的。@DJKRAZE在导出完成之前,计数器
变量实际上不应该增加(出于测试目的,替换为线程.Sleep
)。不同的导出需要不同的时间来完成,这取决于它们包含的数据量,因此我不希望在异步任务开始时预定义值。相反,我尝试使用一个共享变量,该变量在任何导出任务完成时都会增加1。您的代码与示例输出不匹配。我认为,如果您解释一下从哪里获得输出(控制台?或者StatusMessage
是绑定属性?)并在代码中明确说明这一点(在您的示例中,我不认为有任何理由同时使用StatucMessage
和Debug.WriteLine()
。@svick我只需要StatusMessage
更新(是的,是绑定到UI的属性),但在那里添加了调试语句,以便在我开始获得意外结果时进行测试。我添加了“/5”直到我的示例输出的结尾,因为我认为这会更容易理解,但从来没有费心以同样的方式更新调试行。看起来使用Interlocked。Increment对她得到的无序状态报告没有帮助。@alex是正确的,使用Interlocked。Increment
仍然会导致输出顺序错误我怀疑可能是这种情况,谢谢。我使用锁运行了一些测试,现在似乎输出正确