首先关于我的目标:
我正在将一个包含大约1000-5000行的表导入DataTable
。这个绑定到DataGridView
。现在每行必须运行大约5-10秒的过程。完成一个过程后,我想将结果写回DataTabel
(结果列)。
因为这个过程是独立的,所以我想使用多线程加速它。
这是我当前代码的示例结构:
// Will be created for each row
public class FooObject
{
public int RowIndex;
public string Name;
//...
}
// Limiting running tasks to 50
private Semaphore semaphore = new Semaphore(50, 50);
// The DataTable is set up at start-up of the App (columns etc)
private DataTable DtData { get; set; } = new DataTable();
// The button that starts the process
private void btnStartLongRun(object sender, EventArgs e)
{
// some init-stuff
StartRun();
}
private async void StartRun()
{
for (int rowIndex = 0; rowIndex < DtData.Rows.Count)
{
// Creating a task to not block the UI
// Using semaphore here to not create objects
// for all lines before they get in use.
// Having this inside the real task it consumed
// a lot of ram (> 1GB)
await Task.Factory.StartNew(() =>
{
semaphore.WaitOne();
});
// The row to process
var currentRow = DtData.Rows[rowIndex];
// Creating an object from the row-data
FooObject foo = new FooObject()
{
RowIndex = rowIndex;
Name = currentRow["Name"].ToString();
}
// Not awaiting because I want multiple threads
// to run at the same time. The semaphore is
// handling this
TaskScheduler scheduler = TaskScheduler.Current;
Task.Factory.StartNew(() =>
{
// Per-row process
return ProcessFoo(foo);
}).ContinueWith((result) =>
{
FinishProcessFoo(result.Result);
}, CancellationToken.None, TaskContinuationOptions.OnlyOnRanToCompletion, scheduler);
}
}
private FooObject ProcessFoo(FooObject foo)
{
// the actual big process per line
}
private void FinishProcessFoo(FooObject foo)
{
// Locking here because I got broken index errors without
lock(DtGrid.Rows.SyncRoot)
{
// Getting the row that got processed
var procRow = DtData.Rows[foo.RowIndex];
// Writing the result to that row
procRow["Result"] = foo.Result;
// Raising the progressbar
pbData.Value++;
}
// Letting the next task start.
semaphore.Release();
}
最大的问题:
一开始一切都很好。所有线程都运行顺畅并完成其工作。但是,随着应用程序运行的时间越长,它就会越来越没有响应。看起来应用程序正在慢慢开始阻止越来越多。
我开始了5000行的测试运行。它在第2000行附近陷入困境。有时甚至错误都会引发the app isn't responding
。
我在多线程方面没有太多经验。所以也许这段代码非常糟糕。我很感激这里的每一个帮助。我也很乐意将我指向另一个方向以使其更好地运行。
非常感谢。
修改
如果有什么我可以调试来帮助在这里告诉我。
编辑2
我已经启用了所有Common Language Runtime Exceptions
以检查是否存在任何未引发错误的内容。什么都没有。
答案 0 :(得分:1)
如果您想并行处理最多50行,可以考虑使用Parallel.For
,其中MaxDegreeOfParallelism
为50行:
Parallel.For(0, DtData.Rows.Count, new ParallelOptions() { MaxDegreeOfParallelism = 50 }, rowIndex =>
{
//...
});
答案 1 :(得分:0)
为了在信号量上调用WaitOne而开始一项新任务是浪费时间。
您正在使用UI线程来协调数千个异步任务。这是不好的。在新任务中包含对StartRun的调用以避免这种情况。
更好的方法是将行数除以处理器数,然后每个处理器为这些行启动一个任务。那时不需要信号量。