обновить переменную на основе результатов .NET backgroundworker - PullRequest
3 голосов
/ 15 марта 2010

У меня есть программа на C #, которая общается с прибором (анализатором спектра) по сети. Мне нужно иметь возможность изменять большое количество параметров в приборе и считывать их обратно в мою программу. Я хочу использовать backgroundworker для реального разговора с инструментом, чтобы производительность пользовательского интерфейса не пострадала.

Это работает следующим образом: 1) отправьте команду на прибор с новым значением параметра, 2) прочитайте параметр обратно с прибора, чтобы я мог видеть, что на самом деле произошло (например, я пытаюсь установить центральную частоту выше максимальной) что прибор будет обрабатывать, и он говорит мне, что он на самом деле будет обрабатывать), и 3) обновлять программную переменную фактическим значением, полученным от прибора.

Поскольку необходимо обновить довольно много параметров, я бы хотел использовать общую процедуру. Часть, которую я, кажется, не могу понять - обновлять переменную в моем коде тем, что возвращается из инструмента через фонового работника. Если бы я использовал отдельное событие RunWorkerCompleted для каждого параметра, я мог бы напрямую связать обновление с переменной. Я хотел бы придумать способ использования единственной подпрограммы, способной обновлять любые переменные. Все, что я могу придумать, это передать ссылочный номер (различный для каждого параметра) и использовать инструкцию switch в обработчике RunWorkerCompleted для направления результата. Должен быть лучший способ.

Ответы [ 3 ]

1 голос
/ 21 марта 2010

Думаю, я бы передал список параметров, значений и делегатов на BackgroundWorker. Таким образом, вы можете написать код обратного присваивания «синхронно», но отложить выполнение до тех пор, пока значения не будут фактически получены.

Начните с класса «request», который выглядит примерно так:

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

Опять же, это будет работать, потому что на самом деле не выполняется, пока работа не будет завершена.

0 голосов
/ 15 марта 2010

Как насчет этого?

[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";
    }
}
0 голосов
/ 15 марта 2010

[Редактировать - оглянуться на историю обновлений, чтобы увидеть предыдущий ответ. Поговорим о том, что деревья не видны за деревьями]

Есть ли какая-либо причина, по которой вместо передачи ссылочного номера в Background Worker вы не можете передать идентификатор метки, которая должна обновляться при любом возвращаемом значении?

Таким образом, пользовательский интерфейс добавляет элемент в рабочую очередь, содержащий:

  • Переменная для изменения
  • Попытка изменения
  • UI ID

и BackgroundWorker запускает событие с EventArgs, содержащим

  • Попытка изменения
  • Фактическое значение после попытки
  • UI ID
  • Сообщение об ошибке (ноль в случае успеха)

- это вся информация, необходимая для обновления вашего пользовательского интерфейса без переключателя или нескольких аргументов событий, и когда ваш фоновый работник никогда не узнает о деталях пользовательского интерфейса.

...