winforms Parallel.ForEach未更新Windows窗体UI

vmpqdwk3  于 2022-11-17  发布在  Windows
关注(0)|答案(1)|浏览(139)

我有一个windows窗体,在那里我可以输入一次要创建的记录数。如果我提到要创建100万条记录,那么使用for循环的单个线程会花费更多的时间。所以我尝试以100/10的批量创建这100万条记录(可配置)。操作正在进行时,我尝试在UI文本框中更新进度(就像显示许多记录完成)。但问题是,直到调用Parallel.ForEach之前,进度状态文本框都在更新,但在Parallel.ForEach操作中,UI元素没有更新。一旦完成Parallel.ForEach后,只有其更新。

public partial class CreateRecordsForm : Form
{
    private void CreateRecordsBtn_Click(object sender, EventArgs e)
    {
        this.progressStatusTxt.Text = "Started Creating Records"; //Updating in UI
        int count = 0;
        await Task.Run(() => Parallel.ForEach(bulkCreateRequests, new ParallelOptions { MaxDegreeOfParallelism = parallelThreadsSizeInt }, request =>
         {
             dataservice.CreateRecords(request);
             Interlocked.Increment(ref count);
             string progressStatus = count + " of " + bulkCreateRequests.Count + " Requests Completed";
             this.progressStatusTxt.Text = progressStatus; //NOT updating in UI
         }));
        await Task.WhenAll();
        this.progressStatusTxt.Text = "Finished Creating Records"; //Updating in UI
    }
}

我尝试通过创建委托来更新UI,但仍然不起作用。

public delegate void BulkOrderCreateFormDelegate(string status);
//...
public BulkOrderCreateFormDelegate BulkOrderCreateFormCallback;
//...
BulkOrderCreate()
{
    this.BulkOrderCreateFormCallback += new BulkOrderCreateFormDelegate(this.SetProgressStatusValueFn);
}
private void SetProgressStatusValueFn(string progressStatusValue)
{
    if(!string.IsNullOrWhiteSpace(progressStatusValue))
        this.progressStatusTxt.Text = progressStatusValue;
}
private void CreateRecordsBtn_Click(object sender, EventArgs e)
{
    this.progressStatusTxt.Text = "Started Creating Records"; //Updating in UI
    int count = 0;
    await Task.Run(() => Parallel.ForEach(bulkCreateRequests, new ParallelOptions { MaxDegreeOfParallelism = parallelThreadsSizeInt }, request =>
     {
         dataservice.CreateRecords(request);
         Interlocked.Increment(ref count);
         string progressStatus = count + " of " + bulkCreateRequests.Count + " Requests Completed";
         BulkOrderCreateFormCallback(progressStatus); //Getting error : Cross thread operation
     }));
    await Task.WhenAll();
    this.progressStatusTxt.Text = "Finished Creating Records"; //Updating in UI
}

有人知道怎么做吗?你能给我一个工作代码的例子吗?

ecr0jaav

ecr0jaav1#

要报告进度,可以使用Progress<T>类和IProgress<T>接口。在UI线程上示例化Progress<T>类非常重要,因为它在示例化期间捕获当前SynchronizationContext,并使用捕获的上下文传播进度消息。

public partial class CreateRecordsForm : Form
{
    private async void CreateRecordsBtn_Click(object sender, EventArgs e)
    {
        IProgress<string> progress = new Progress<string>(
            message => this.progressStatusTxt.Text = message);
        progress.Report("Started Creating Records");
        int count = 0;
        ParallelOptions options = new() { MaxDegreeOfParallelism = parallelThreadsSizeInt };
        await Task.Run(() => Parallel.ForEach(bulkCreateRequests, options, request =>
        {
            dataservice.CreateRecords(request);
            int localCount = Interlocked.Increment(ref count);
            string progressStatus = localCount + " of " +
                bulkCreateRequests.Count + " Requests Completed";
            progress.Report(progressStatus);
        }));
        progress.Report("Finished Creating Records");
    }
}

UI控件与UI线程相似。它们应该在此线程上以独占方式进行操作。

相关问题