信号量的多线程 |应用程序没有响应

Multithreading with Semaphore | App not responding

先说说我的目标:

我正在将大约 1000-5000 行的 table 导入到 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();
}

大问题:

一开始一切正常。所有线程都 运行 正在顺利完成工作。但是应用程序 运行s 越长,它就会变得无响应。看起来应用程序正在慢慢开始阻止越来越多的人。

我开始了一个测试-运行 有 5000 行。它卡在第 2000 行左右。有时甚至会引发错误 the app isn't responding

我在多线程方面经验不多。所以也许这段代码是完全糟糕的。我感谢这里的每一个帮助。我也很乐意为我指明另一个方向,让这个 运行 变得更好。

非常感谢。

编辑
如果这里有任何我可以调试的帮助,请告诉我。

编辑 2
我已经启用所有 Common Language Runtime Exceptions 以检查是否有任何未引发错误的内容。没有。

如果您想并行处理多达 50 行,您可以考虑使用 Parallel.ForMaxDegreeOfParallelism 为 50:

Parallel.For(0, DtData.Rows.Count, new ParallelOptions() { MaxDegreeOfParallelism = 50 }, rowIndex => 
{
    //...
});
  1. 启动一个新任务只是为了在信号量上调用 WaitOne 是浪费时间。

  2. 您正在使用 UI 线程来协调数千个异步任务。这是不好的。在新任务中包装对 StartRun 的调用以避免这种情况。

  3. 更好的方法是将行数除以处理器数,然后仅针对这些行为每个处理器启动一个任务。那么就不需要信号量了。