请帮我使此代码线程安全 [英] Please help me make this code thread safe

查看:164
本文介绍了请帮我使此代码线程安全的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个位,且让我的数据加载和过滤线程安全的问题。

I've got a bit of a problem with making my data loading and filtering thread safe.

这是我的控件的基类,下面的代码负责处理所有数据通过BackgroundWorker的人口。这往往扔在了错误this.DataWorker.RunWorkerAsync()的说法,BackgroundWorker的正忙。

The following code on my control's base class which handles all the data population through a BackgroundWorker. This tends to throw the error on "this.DataWorker.RunWorkerAsync()" saying that the BackgroundWorker is busy.

/// <summary>
/// Handles the population of the form data.
/// </summary>
/// <param name="reload">Whether to pull data back from the WebService.</param>
public void Populate(bool reload)
{
    if (!this.DataWorker.IsBusy)
    {

        // Disable the filter options
        IvdSession.Instance.FilterManager.SetEnabledState(this.GetType(), false);

        // Perform the population
        this.DataWorker.RunWorkerAsync(reload);

    }
    else if (!reload)
    {
        // If the data worker is busy and this is a not reload, then something bad has happened (i.e. the filter has run during a reload.)
        throw new InvalidOperationException("The DataWorker was busy whilst asked to reload.");
    }
}



中的代码被称为两个可能的地方。首先在窗体上的计时器控件是:

The code is called in two possible places. Firstly by a timer on the form that the control is on:

private void tmrAutoRefresh_Tick(object sender, EventArgs e)
{
    if (!(this.CurrentBody == null))
    {
        this.CurrentBody.Populate(true);
    }
}



其次,任何时候用户选择滤波器选项从一些下拉列表:

And secondly, any time a user selects a Filter Option from a number of drop down lists:

public void Filter()
{
    if (!m_BlockFilter)
    {
        IvdInstance.Main.CurrentBody.FirstRun = true;
        IvdInstance.Main.CurrentBody.Populate(false);
    }
}



在主窗体上的定时器运行,每60秒通过真实的填充方法。传递重装作为trues告诉BackgroundWorker的,它需要从WebService拉下一组新的数据:

The Timer on the main form runs every 60 seconds and passes true to the Populate method. Passing reload as trues tells the BackgroundWorker that it needs to pull down a fresh set of data from the WebService:

void dataWorker_DoWork(object sender, DoWorkEventArgs e)
{

    try
    {

        if (base.FirstRun)
        {
            base.CleanListView();
        }

        if ((bool)e.Argument)
        {
            byte[] serialized = IvdSession.DataAccess.GetServiceCalls(IvdSession.Instance.Company.Description, IvdSession.Instance.Company.Password, null);
            m_DataCollection = new DalCollection<ServiceCallEntity>(serialized);
        }

        List<ServiceCallEntity> collection = this.ApplyFilter();
        base.HandlePopulation<ServiceCallEntity>(collection, e);

    }
    catch (WebException ex)
    {
        // Ignore - Thrown when user clicks cancel
    }
    catch (System.Web.Services.Protocols.SoapException ex)
    {
        // Log error on server and stay transparent to user
        base.LogError(ex);
    }
    catch (System.Data.SqlClient.SqlException ex)
    {
        // Inform user that the database is unavailable
        base.HandleSystemUnavailable(ex);
    }

}



据我所知当我设法单击完全相同的时间定时器触发的人口事件过滤选项出现错误。我想有东西从填充方法缺失,即锁,但我不能确定如何在这种情况下正确地使用它。

As far as I'm aware, the error occurs when I manage to click a filter option at exactly the same time the Timer fires the population event. I figure there is something missing from the Populate method, i.e. a lock, but I'm unsure as to how to use it correctly in this instance.

中的代码青睐朝向用户的输入。如果用户选择过滤选项,自动更新应该被阻止,如果自动更新大火然后过滤器选项暂时禁用。如果他们开火的同时,用户输入应该得到优先级(如果可能)。

The code is favoured towards the user input. If a user selects a filter option, the auto update should be blocked, if the auto update fires then the filter options are temporarily disabled. If they fire at the same time, the user input should get priority (if possible).

希望有人能帮助!

推荐答案

首先,添加在你的填充方法体锁:

First, add a lock around your Populate method body:

private object _exclusiveAccessLock = new object();
public void Populate(bool reload)
{
    lock (_exclusiveAccessLock)
    {
         // start the job
    }
}

这将帮助您避免竞争条件(虽然:如果我这样做是正确,因为你使用的是Windows.Forms的定时器,它总是从GUI线程火,所以他们不应该被执行的究竟的在同一时间)。

This will help you avoid a race condition (although: if I got it right, since you are using a Windows.Forms Timer, it will always fire from the Gui thread, so they should never be executed exactly at the same time).

接下来,我不知道你是否应该抛出异常的。你可以,例如,设置一个额外的标志,显示你的工人还没有完成,但这是 IsBusy 应该告诉你啦。

Next, I am not sure if you should throw the exception at all. You can, for example, set an additional flag that shows you that the worker hasn't finished yet, but that is what IsBusy should tell you anyway.

再有就是 m_BlockFilter 标记。我看不到你在哪里设置它。它应该在锁内设置也,而不是在后台线程,因为在这种情况下,你不能确定它不会被延迟。您还需要使该字段挥发性如果你要使用它作为一个跨线程标记。

Then there is the m_BlockFilter flag. I cannot see where you are setting it from. It should be set inside the lock also, not in the background thread, because in that case you cannot be sure that it will not be delayed. You also need to make the field volatile if you are going to use it as a cross-thread flag.

这篇关于请帮我使此代码线程安全的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆