在后台线程正确加载文档

在后台线程正确加载文档

本文介绍了在后台线程正确加载文档的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是我写的应用程序和一个我继承,我有一个愿望继续更好地了解在后台线程加载数据的线程安全问题。假设我有一个简单,单一窗口的Windows窗体应用程序使用Load按钮和的BackgroundWorker





按钮的点击处理程序调用 loadBackgroundWorker.RunWorkerAsync(),以及工人的的DoWork 处理程序创建和初始化类型文件其中,加载后,它存储在窗体的 LoadedDocument 属性的对象。在工人的 RunWorkerCompleted 处理程序,一个的MessageBox 显示 LoadedDocument 。我知道这一切很难想象,所以我包括完整的代码。对不起,它使这么长时间阅读问题



这里的窗体的代码:



 使用系统; 
使用System.ComponentModel;使用System.Windows.Forms的
;

命名空间BackgroundLoadTest
{
公共部分Form1类:表格
{
私人文件_loadedDocument;
公开文件LoadedDocument
{
得到
{
锁(本)
{
返回_loadedDocument;
}
}

{
锁(本)
{
_loadedDocument =价值;
}
}
}

公共Form1中()
{
的InitializeComponent();
loadBackgroundWorker.DoWork + =新DoWorkEventHandler(loadBackgroundWorker_DoWork);
loadBackgroundWorker.RunWorkerCompleted + =新RunWorkerCompletedEventHandler(loadBackgroundWorker_RunWorkerCompleted);
}

无效loadBackgroundWorker_DoWork(对象发件人,DoWorkEventArgs E)
{
文档D =新的文件();
d.Property1 =测试;
d.Property2 = 1;
d.Property3 = 2;
this.LoadedDocument = D;
}

无效loadBackgroundWorker_RunWorkerCompleted(对象发件人,RunWorkerCompletedEventArgs E)
{
MessageBox.Show(加载Property1 =文件+
LoadedDocument.Property1 +,Property2 =+
LoadedDocument.Property2 +,Property3 =+
LoadedDocument.Property3);
}

私人无效loadButton_Click(对象发件人,EventArgs五)
{
loadBackgroundWorker.RunWorkerAsync();
}
}
}



下面的代码为文件类:

 使用系统; 

命名空间BackgroundLoadTest
{
公共类文件
{
公共字符串Property1 {搞定;组; }
公共双Property2 {搞定;组; }
公众诠释Property3 {搞定;组; }
}
}



我的问题是:



什么线程安全/存储可见性的问题,你用这个代码中看到的,或者你会做什么不同的给定负载数据在后台线程,并最终使用加载的数据目标在UI线程?



LoadedDocument 属性足以锁定,以确保数据初始化在后台线程将是UI线程可见?是锁定必要吗?我真的想了解在后台线程加载复杂的文档,同时保持GUI响应看似很常见的问题,我知道这是棘手的问题。



编辑:是清楚,我最关心的是这里存储的知名度。我想,以确保工人完成时全部由后台线程完成的数据初始化成为可见的GUI线程。我不想陷入生活的CPU缓存的变化和剩余看不见其他CPU线程。我不知道怎么说出我的顾虑更好,因为他们仍然相当模糊了我。


解决方案

This is plain wrong. Locking introduces memory barriers and thereby prevents instruction reordering and makes cached values visible to other threads. Accessing fields or properties (which also access fields) from different threads without synchronization isn't guaranteed to always work and can't be considered correct code.

What you're doing is accessing the LoadedDocument property from both your background thread and your UI thread. As you have implemented locking in there, this is correct code and will be thread safe.

The DoWorkEventArgs argument in your loadBackgroundWorker_DoWork method has a Result property which should be used to set the result of the background work. The RunWorkerCompletedEventArgs.Result property then can be used to access this value. Try the following:

    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 is one of the most comprehensive and understandable resources in regard to multithreading in .NET which I would highly recommend. Your question would have been answered here.


Edit: Clarification of how BackgroundWorker synchronizes stuff

Looking into reference source of Background worker, it is not really obvious how the result is synchronized between threads:

    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);
    }

This happens on the background thread. The last line then marshals back to the UI thread. Looking further down the stack, there are no lock statements or other synchronization directives there. So how is this made thread safe?

Looking into the RunWorkerCompletedEventArgs, we find no synchronization code either. But there is some strange attribute over there:

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

MSDN explains:

So putting this attribute above your class obviously makes its members thread safe by synchronizing their access. Is this awesome? I think so. Should you use this in your code? Probably not.

这篇关于在后台线程正确加载文档的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-11 19:13