C # Многопоточное мультиклассовое графическое приложение, я делаю это правильно? - PullRequest
2 голосов
/ 31 октября 2010

Я создал следующий проект, чтобы показать вам, ребята, как я планирую делать вещи.Но мой основной проект будет больше и будет иметь несколько классов.Я просто пытаюсь заставить это работать должным образом, поэтому я знаю, что при написании кода я использую хорошую практику.

хорошо, давайте начнем :), поэтому в моей форме есть кнопка с именем "button1" и текстовое поле.называется "textBox1", и у меня есть класс с именем "Class1", который имеет метод "testtest ()", я просто поместил Thread.Sleep в метод testtest, чтобы я мог выяснить, работает ли он в другом потоке.

Вот мой код формы

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace Test
{
  public partial class Form1 : Form
  {
    public Form1()
    {
      InitializeComponent();
    }

    delegate void Class1Deligate();

    private void button1_Click(object sender, EventArgs e)
    {
      Class1 c = new Class1();
      Class1Deligate testMethod = new Class1Deligate(c.testtest);
      testMethod.BeginInvoke(endTestMethod, testMethod);
    }

    void endTestMethod(IAsyncResult ar)
    {

    }
  }
}

, а вот мой код первого класса

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows.Forms;

namespace Test
{
  class Class1
  {
    public void testtest()
    {
      Thread.Sleep(8888);
      MessageBox.Show("Done");
    }
  }
}

Правильно ли я создаю новую тему?А может кто-нибудь подскажите, пожалуйста, как обновить textbox1 из метода testtest в class1 во время его работы?Я сделал более раннюю публикацию, и мне сказали использовать Dispatcher, но по некоторым причинам кажется, что класс Dispatcher для меня недоступен.

С наилучшими пожеланиями

Ответы [ 4 ]

1 голос
/ 31 октября 2010

Я не думаю, что вы на самом деле запускаете новый поток. delegate.BeginInvoke, вероятно, использует внутренний ThreadPool, чтобы делать ставки. И как примечание: я не думаю, что вам следует звонить BeginInvoke без соответствующего EndInvoke вызова после него (в противном случае ссылка на IAsyncResult, возвращаемая из вызова BeginInvoke, не будет выпущена). 1008 *

Если вам нужен новый поток, вы можете создать новый, используя класс Thread .

Thread t = new Thread(c.testtest);
t.IsBackground = true; // end if main thread ends
t.Start();

Обратите внимание, что если вы используете класс Thread, как описано выше, и используете GUI, вам необходимо присоединить метод к потоку GUI для его обновления. Вы можете сделать это с помощью вызова Control.Invoke

public void updateGUI(object state) {
    if (control.InvokeRequired) {
        control.Invoke(new Action<object>(updateGUI), state);
        return;
    }
    // if we are here, we can safely update the GUI
}
0 голосов
/ 01 ноября 2010

В зависимости от того, что вы пытаетесь достичь, есть пара различных способов, которыми вы можете пойти.Если у вас есть одна задача, которую вы хотите выполнить без блокировки потока пользовательского интерфейса, вы можете использовать BackgroundWorker .Это даст вам очень простой способ сохранить ваш пользовательский интерфейс быстрым и упростит обновление пользовательского интерфейса без необходимости вызова Invoke и т. Д.

Если вы собираетесь запускать много задач, вам, вероятно, следует использовать1007 * ThreadPool .Если вам нужно назначить приоритет потокам или они будут тратить много времени на блокировку, вам следует создать свои собственные потоки экземпляров.

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

0 голосов
/ 31 октября 2010

Arya

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

    void button1_Click(object sender, EventArgs e)
    {
        Class1 c = new Class1();
        Action action = c.Test;

        action.BeginInvoke(new AsyncCallback(EndTestMethod), null);
    }
    void EndTestMethod(IAsyncResult token)
    {    
        Action callBack = (Action)((AsyncResult)token).AsyncDelegate;
        callBack.EndInvoke(token);
    }

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

 Thread t = new Thread(c.Test);
        t.IsBackground = true;
        t.Start();

Однако передача обратного вызова вызывающему потоку через обратный вызов станет более длительной, поскольку вам придется самостоятельно писать этот код.

Что касается Диспетчера, этокласс WPF, который ставит в очередь действия, которые должны обрабатываться потоком пользовательского интерфейса в приложениях WPF (среди прочего).Судя по вашему коду, вы пишете приложение Winforms, поэтому этот тип не подходит.

Наконец, для обновления текстового поля необходимо проверить, пытаетесь ли вы получить доступ к этому элементу управления натот же поток, который создал элемент управления (поток пользовательского интерфейса).Это делается через control.invokerequired и control.invoke.Как следует:

       void AddText(string text)
    {
        if (textBox1.InvokeRequired)
        {
            textBox1.Invoke((Action<string>)AddText, text);
        }
        textBox1.Text = text;
    }
0 голосов
/ 31 октября 2010

Во-первых, есть класс ThreadPool , доступный для потоков, если хотите.Это то, что я обычно использую для запуска новых потоков, например,

ThreadPool.QueueUserWorkItem(f.ThreadPoolCallback, i);

Ссылка объясняет их немного подробнее.Что касается графического интерфейса, вам нужно будет проверить, является ли текущий поток тем же потоком, что и поток пользовательского интерфейса (так как только пользовательский поток может обновлять пользовательский интерфейс), и, если это не так, вызвать функцию вПользовательский интерфейсЭто можно сделать следующим образом:

delegate void ChangeFormUnsafeCallback(string text);
private void ChangeFormUnsafe(string text)
{
  // Do stuff to form/controls
}

private void SomeFunction()
{      
  myForm.Invoke(new ChangeFormUnsafeCallback(ChangeFormUnsafe), "foo");
}

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...