根据.NET backgroundworker的结果更新变量

时间:2010-03-14 23:51:17

标签: c# backgroundworker asynchronous

我有一个C#程序通过网络与仪器(频谱分析仪)通信。我需要能够更改仪器中的大量参数并将它们读回我的程序中。我想使用backgroundworker与仪器进行实际交谈,以便UI性能不受影响。

其工作方式是 - 1)使用新参数值向仪器发送命令,2)从仪器返回读取参数,以便我可以看到实际发生的情况(例如,我尝试将中心频率设置为高于最大值仪器将处理它并告诉我它将实际处理的是什么),以及3)用从仪器接收的实际值更新程序变量。

因为有很多参数需要更新,所以我想使用通用例程。我似乎无法理解我的大脑的部分是通过后台工作者从仪器返回的内容更新我的代码中的变量。如果我为每个参数使用单独的RunWorkerCompleted事件,我可以将更新直接硬连接到变量。我想提出一种使用能够更新任何变量的单个例程的方法。我能想到的就是传递一个引用号(每个参数不同),并在RunWorkerCompleted处理程序中使用switch语句来指示结果。必须有更好的方法。

3 个答案:

答案 0 :(得分:1)

我认为我要做的是将参数列表,值,和委托传递给BackgroundWorker。这样,您可以“同步”编写回传代码,但执行延迟直到实际检索到值。

从“请求”类开始,类似于:

class ParameterUpdate
{
    public ParameterUpdate(string name, string value, Action<string> callback)
    {
        this.Name = name;
        this.Value = value;
        this.Callback = callback;
    }

    public string Name { get; private set; }
    public string Value { get; set; }
    public Action<string> Callback { get; private set; }
}

然后编写异步代码以使用它:

private void bwUpdateParameters_DoWork(object sender, DoWorkEventArgs e)
{
    var updates = (IEnumerable<ParameterUpdate>)e.Argument;
    foreach (var update in updates)
    {
        WriteDeviceParameter(update.Name, update.Value);
        update.Value = ReadDeviceParameter(update.Name);
    }
    e.Result = updates;
}

private void bwUpdateParameters_RunWorkerCompleted(object sender,
    RunWorkerCompletedEventArgs e)
{
    var updates = (IEnumerable<ParameterUpdate>)e.Argument;
    foreach (var update in updates)
    {
        if (update.Callback != null)
        {
            update.Callback(update.Value);
        }
    }
}

以下是您启动更新的方式。假设您有一堆成员字段要使用所用参数的实际值进行更新:

// Members of the Form/Control class
private string bandwidth;
private string inputAttenuation;
private string averaging;

// Later on, in your "update" method
var updates = new List<ParameterUpdate>
{
    new ParameterUpdate("Bandwidth", "3000", v => bandwidth = v),
    new ParameterUpdate("InputAttenuation", "10", v => inputAttenuation = v),
    new ParameterUpdate("Averaging", "Logarithmic", v => averaging = v)
};
bwUpdateParameters.RunWorkerAsync(updates);

这就是你要做的一切。所有实际工作都是在后台完成的,但是您正在编写简单的变量赋值语句,就好像它们位于前台一样。代码简短,完全是线程安全的,因为实际的分配是在RunWorkerCompleted事件中执行的。

如果你需要做更多的事情,比如变量之外的更新控制,它很简单,你可以把你想要的任何东西用于回调,即:

new ParameterUpdate("Bandwidth", "3000", v =>
{
    bandwidth = v;
    txtBandwidth.Text = v;
})

同样,这将有效,因为在工作完成之前它实际上并未执行。

答案 1 :(得分:0)

[编辑 - 回顾更新历史记录以查看上一个答案。谈论无法看到树木的木材]

是否有任何理由不是将参考号传递给后台工作者,而是不能传递应该使用传回的任何值更新的标签的ID?

因此,UI会在工作队列中添加一个包含以下内容的项目:

  • 要更改的变量
  • 尝试更改
  • UI ID

并且BackgroundWorker触发包含

的EventArgs的事件
  • 尝试更改
  • 尝试后的实际值
  • UI ID
  • 错误消息(如果成功则为null)

这是在没有开关或多个事件参数的情况下更新UI所需的所有信息,并且没有您的后台工作人员了解UI细节。

答案 2 :(得分:0)

这样的事情怎么样?

[TestFixture]
public class BGWorkerTest
{
    string output1;
    string output2;

    [Test]
    public void DoTest()
    {
        var backgroundWorker = new BackgroundWorker();
        backgroundWorker.DoWork += (sender, args) =>
                                   {
                                       output1 = DoThing1();
                                       output2 = DoThing2();
                                   };
        backgroundWorker.RunWorkerAsync();
        //Wait for BG to finish
        Thread.Sleep(3000);
        Assert.AreEqual("Thing1",output1);
        Assert.AreEqual("Thing2",output2);
    }

    public string DoThing1()
    {
        Thread.Sleep(1000);
        return "Thing1";
    }
    public string DoThing2()
    {
        Thread.Sleep(1000);
        return "Thing2";
    }
}