Обновите UI от нескольких рабочих потоков (.NET)

У меня есть любимый проект, что я работаю над этим, имеет несколько рабочих потоков. Вывод всего к консоли становится трудным следовать, таким образом, я хочу разработать UI, который будет иметь одну область вывода на поток. Я хочу знать, что лучший способ для потоков отправляет обновления UI. У меня есть две идеи:

1) Имейте каждый поток, устанавливает флаг "DataUpdated", когда новые данные доступны, и имейте UI, периодически проверяют на новые данные.

2) Создайте каждый поток с обратным вызовом к Обновлению UI (...) метод, который назовут, когда новые данные станут доступными.

Я в настоящее время склоняюсь (2) по двум причинам: Мне не нравится идея "проверить" каждый поток, и потому что это - мое первое многопоточное приложение, и (2) кажется более простым, чем это, вероятно. Я хочу знать:

  • Какая опция предпочтительна с точки зрения простоты и эффективности?
  • У Вас есть какие-либо подсказки для реализации (2) или что-то как он (т.е. более событийно-ориентированный)?
6
задан iandisme 19 January 2010 в 21:35
поделиться

6 ответов

Я голосую также за # 2, но с помощью BackgroundWorkers вместо System.Threading.Threads.

-121--4213543-

Странно?

(value & 0x1) > 0

Делится ли он на два (четные)?

(value & 0x1) == 0
-121--604484-

Можно легко реализовать (2), создав компоненты BackgroundWorker и выполнив работу в их обработчиках DoWork:

BackgroundWorker bw = new BackgroundWorker();
bw.WorkerReportsProgress = true;
bw.DoWork += /* your background work here */;
bw.ProgressChanged += /* your UI update method here */;
bw.RunWorkerAsync();

Каждый BackgroundWorker может сообщать о ходе выполнения в поток пользовательского интерфейса, вызывая EventProgress: хотя это в первую очередь предназначено для отчетности о ходе выполнения ограниченного процесса, это не обязательно - вы также можете передать собственные пользовательские данные, если для этого требуется обновление пользовательского интерфейса. Из обработчика DoWork можно вызвать EventProgress.

Хорошая вещь в BackgroundWorker заключается в том, что он заботится о множестве грязных деталей перекрестной многопоточности для вас. Он также соответствует управляемой событиями модели обновлений, которые вы (правильно) предпочитаете явным обратным вызовам.

12
ответ дан 8 December 2019 в 17:21
поделиться

В большинстве случаев в большинстве случаев проще всего можно использовать компонент BackgroundWorker, как это предложено в ответе Itowlson, и я бы настоятельно рекомендую использовать этот подход, если это возможно. Если по какой-то причине вы не можете использовать компонент FlowerWorker для вашей цели, например, если вы разрабатываете с .NET 1.1 (Yikes!) Или с компактными рамками, то вам может потребоваться использовать альтернативный подход:

При элементах управления WinForm вы должны избегать модификации элементов управления на любой резьбе, отличной от потока, который изначально создал элемент управления. Компонент Backgroundworker обрабатывает это для вас, но если вы не используете это, то вы можете и должны использовать INVERCEREQUEDIED ​​имущественный метод и включенный в систему System.Windows.Forms.Control Class. Ниже приведен пример, который использует это свойство и способ:

public partial class MultithreadingForm : Form
{
    public MultithreadingForm()
    {
        InitializeComponent();
    }

    // a simple button event handler that starts a worker thread
    private void btnDoWork_Click(object sender, EventArgs e)
    {
        Thread t = new Thread(WorkerMethod);
        t.Start();
    }

    private void ReportProgress(string message)
    {
        // check whether or not the current thread is the main UI thread
        // if not, InvokeRequired will be true
        if (this.InvokeRequired)
        {
            // create a delegate pointing back to this same function
            // the Invoke method will cause the delegate to be invoked on the main UI thread
            this.Invoke(new Action<string>(ReportProgress), message);
        }
        else
        {
            // txtOutput is a UI control, therefore it must be updated by the main UI thread
            if (string.IsNullOrEmpty(this.txtOutput.Text))
                this.txtOutput.Text = message;
            else
                this.txtOutput.Text += "\r\n" + message;
        }
    }

    // a generic method that does work and reports progress
    private void WorkerMethod()
    {
        // step 1
        // ...
        ReportProgress("Step 1 completed");

        // step 2
        // ...
        ReportProgress("Step 2 completed");

        // step 3
        // ...
        ReportProgress("Step 3 completed");
    }
}
1
ответ дан 8 December 2019 в 17:21
поделиться

Я голосую за № 2, но с фоновыми работами вместо System.threading.threads.

1
ответ дан 8 December 2019 в 17:21
поделиться

Предпочтительным способом реализации многопоточничества в вашем приложении является использование компонента Projan Worker . Компонент Backgroundworker использует модель, управляемую событиями для многопотативной обработки. Рабочий поток запускает ваш обработчик событий Dowork, а поток, который создает ваши элементы управления, выполняет ваши ProgressChanged и Run WorkerCompleted Deadlers.
Когда вы обновляете свои элементы управления пользовательскими интерфейсами в Progresschanged EventHandler, они автоматически обновляются на главной ните, который помешает вам получить исключения Crossthread.

Посмотрите здесь Например, как использовать Flain Worker.

0
ответ дан 8 December 2019 в 17:21
поделиться

Если вы создаете свои собственные потоки (неправочные работы или потоки Threadpool), вы можете пройти метод обратного вызова из основного потока, который вызывается из рабочего потока. Это также позволяет передавать аргументы обратным вызовам и даже вернуть значение (например, флаг GO / NO-GO). В вашем обратном вызове вы обновляете пользовательский интерфейс через диспетчеру целевого элемента управления:

public void UpdateUI(object arg)
{
    controlToUpdate.Dispatcher.BeginInvoke(
        System.Windows.Threading.DispatcherPriority.Normal
        , new System.Windows.Threading.DispatcherOperationCallback(delegate
        {
            controToUpdate.property = arg;
            return null;
        }), null);
    }
} 
0
ответ дан 8 December 2019 в 17:21
поделиться

Вы можете использовать ваши рабочие потоки поднимать события и иметь основную ниту UI добавить обработчики событий. Вам нужно быть осторожным, вы не поднимаете слишком много событий, так как он может стать уродливым, если ваши рабочие потоки поднимают несколько событий в секунду.

Эта статья дает быстрый обзор.

0
ответ дан 8 December 2019 в 17:21
поделиться
Другие вопросы по тегам:

Похожие вопросы: