gpt4 book ai didi

c# - 在后台线程上正确加载文档

转载 作者:太空狗 更新时间:2023-10-30 01:35:03 26 4
gpt4 key购买 nike

从我编写的应用程序和我继承的应用程序中,我一直希望更好地理解在后台线程上加载数据的线程安全问题。假设我有一个简单的单窗口 Windows 窗体应用程序,带有一个“加载”按钮和一个 BackgroundWorker :

My App in the Visual Studio Designer

按钮的 Click处理程序调用 loadBackgroundWorker.RunWorkerAsync() ,以及 worker 的 DoWork处理程序创建并初始化类型为 Document 的对象加载后,它存储在表单的 LoadedDocument 中属性(property)。在 worker 的RunWorkerCompleted处理程序,一个 MessageBox显示 LoadedDocument 的属性.我知道这一切都很难想象,所以我包含了完整的代码。抱歉,这个问题读起来太长了。

表单代码如下:

using System;
using System.ComponentModel;
using System.Windows.Forms;

namespace BackgroundLoadTest
{
public partial class Form1 : Form
{
private Document _loadedDocument;
public Document LoadedDocument
{
get
{
lock (this)
{
return _loadedDocument;
}
}
set
{
lock (this)
{
_loadedDocument = value;
}
}
}

public Form1()
{
InitializeComponent();
loadBackgroundWorker.DoWork += new DoWorkEventHandler(loadBackgroundWorker_DoWork);
loadBackgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(loadBackgroundWorker_RunWorkerCompleted);
}

void loadBackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
Document d = new Document();
d.Property1 = "Testing";
d.Property2 = 1;
d.Property3 = 2;
this.LoadedDocument = d;
}

void loadBackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
MessageBox.Show("Document loaded with Property1 = " +
LoadedDocument.Property1 + ", Property2 = " +
LoadedDocument.Property2 + ", Property3 = " +
LoadedDocument.Property3);
}

private void loadButton_Click(object sender, EventArgs e)
{
loadBackgroundWorker.RunWorkerAsync();
}
}
}

这是 Document 的代码类:

using System;

namespace BackgroundLoadTest
{
public class Document
{
public string Property1 { get; set; }
public double Property2 { get; set; }
public int Property3 { get; set; }
}
}

我的问题是:

您发现此代码存在哪些线程安全/内存可见性问题,或者考虑到在后台线程上加载数据并最终在 UI 线程上使用加载数据的目标,您会采取哪些不同的做法?

是锁定在LoadedDocument足以确保在后台线程中初始化的数据对 UI 线程可见的属性?是否需要锁定?我真的很想了解在保持 GUI 响应的同时在后台线程上加载复杂文档这一看似非常普遍的问题,而且我知道这是棘手的事情。

编辑:明确地说,我在这里最关心的是内存可见性。我想确保后台线程完成的所有数据初始化在工作程序完成时对 GUI 线程可见。我不希望更改卡在 CPU 缓存中并且对其他 CPU 上的线程不可见。我不知道如何更好地表达我的担忧,因为它们对我来说仍然相当模糊。

最佳答案

Locking around your getters and setters do nothing, assigning a reference type to a variable is an atomic operation.

这是完全错误的。锁定引入了内存屏障,从而防止指令重新排序并使缓存的值对其他线程可见。从不同步的不同线程访问字段或属性(也访问字段)不能保证始终有效,不能被视为正确的代码

您正在做的是从后台线程和 UI 线程访问 LoadedDocument 属性。因为您已经在其中实现了锁定,所以这是正确的代码并且是线程安全的。

loadBackgroundWorker_DoWork 方法中的 DoWorkEventArgs 参数有一个 Result 属性,应该用来设置后台工作的结果。然后可以使用 RunWorkerCompletedEventArgs.Result 属性访问该值。尝试以下操作:

    void loadBackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
Document d = new Document();
d.Property1 = "Testing";
d.Property2 = 1;
d.Property3 = 2;
e.Result = d;
}

void loadBackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
this.LoadedDocument = (Document)e.Result;
MessageBox.Show("Document loaded with Property1 = " +
LoadedDocument.Property1 + ", Property2 = " +
LoadedDocument.Property2 + ", Property3 = " +
LoadedDocument.Property3);
}

This tutorial是关于 .NET 中多线程的最全面和最易理解的资源之一,我强烈推荐。你的问题会得到回答here .


编辑:阐明 BackgroundWorker 如何同步内容

Still, I'm curious what magic goes on in BackgroundWorker that makes data passed via e.Result fully visible to the GUI thread.

调查reference source of Background worker , 结果如何在线程之间同步并不是很明显:

    private void WorkerThreadStart(object argument)
{
object workerResult = null;
Exception error = null;
bool cancelled = false;

try
{
DoWorkEventArgs doWorkArgs = new DoWorkEventArgs(argument);
OnDoWork(doWorkArgs);
if (doWorkArgs.Cancel)
{
cancelled = true;
}
else
{
workerResult = doWorkArgs.Result;
}
}
catch (Exception exception)
{
error = exception;
}

RunWorkerCompletedEventArgs e =
new RunWorkerCompletedEventArgs(workerResult, error, cancelled);

asyncOperation.PostOperationCompleted(operationCompleted, e);
}

这发生在后台线程上。然后最后一行编码回 UI 线程。再往下看堆栈,那里没有锁定语句或其他同步指令。那么这如何使线程安全呢?

调查 RunWorkerCompletedEventArgs ,我们也没有发现同步代码。但是那里有一些奇怪的属性:

[HostProtection(SharedState = true)]
public class RunWorkerCompletedEventArgs : AsyncCompletedEventArgs

MSDN解释:

When SharedState is true, it indicates that a state is exposed that might be shared between threads.

因此,将此属性放在您的类之上显然可以通过同步访问使其成员线程安全。这很棒吗?我认同。你应该在你的代码中使用它吗?可能不是。

关于c# - 在后台线程上正确加载文档,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27573743/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com