Проблема с потоками в C # - PullRequest
0 голосов
/ 30 ноября 2009

Чтобы поиграть с многопоточностью, делегатами и специалистами по фону, я собираю несколько небольших приложений, у меня возникли небольшие проблемы с одним из них. У меня есть форма Windows, с текстовым полем, кнопкой и richttext. Когда я нажимаю кнопку, текст в текстовом поле используется в качестве параметра для создания экземпляра класса, например:

public partial class Form1 : Form
{
    private BackgroundWorker backgroundWorker;

    public Form1()
    {
        InitializeComponent();            
    }

    private void button1_Click(object sender, EventArgs e)
    {   
        backgroundWorker = new BackgroundWorker();
        backgroundWorker.DoWork += new DoWorkEventHandler(worker_DoWork);
        backgroundWorker.RunWorkerAsync();
    }

    void worker_DoWork(object sender, DoWorkEventArgs e)
    {
        new Thread((ThreadStart)delegate()
        {
            this.BeginInvoke((ThreadStart)delegate()
            {
                foreach (string line in textBox1.Lines)
                {  
                    Dig digger = new Dig(line, textBox1.Text);
                    digger.DomainChecked += new Dig.DomainCheckedHandler(OnUpdateTicker);

                    string response = digger.GetAllInfo();

                    richTextBox1.AppendText(response);
                    Application.DoEvents();
                }
            });
        }).Start();
    }

    void OnUpdateTicker(string msg)
    {
        new Thread((ThreadStart)delegate()
        {
            this.BeginInvoke((ThreadStart)delegate()
            {
                label4.Text = msg;
                Application.DoEvents();
            });
        }).Start();            
    }
}

При отладке я столкнулся с textBox1.Lines, выдавшим исключение типа «Microsoft.VisualStudio.Debugger.Runtime.CrossThreadMessagingException» Любые советы, как решить эту проблему?

Ответы [ 3 ]

5 голосов
/ 30 ноября 2009

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

void worker_DoWork(object sender, DoWorkEventArgs e)
{
    foreach (string line in textBox1.Lines)
    {  
        Dig digger = new Dig(line, textBox1.Text);
        digger.DomainChecked += new Dig.DomainCheckedHandler(OnUpdateTicker);
        string response = digger.GetAllInfo();
        richTextBox1.Invoke((Action) delegate { richTextBox1.AppendText(response); });
    }
}

Обратите внимание, что код явно не создает никаких новых потоков, а также как вызов метода AppendText выполняется с помощью вызова Control.Invoke, заставляя его выполняться в потоке пользовательского интерфейса.

0 голосов
/ 30 ноября 2009

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

From: http://www.albahari.com/threading/part3.aspx

Control.Invoke

В многопоточном приложении Windows Forms запрещено вызывать метод или свойство элемента управления из любого потока, кроме того, который его создал. Все вызовы между потоками должны быть явно перенаправлены в поток, создавший элемент управления (обычно основной поток), с использованием метода Control.Invoke или Control.BeginInvoke. Нельзя полагаться на автоматическое маршалинг, потому что это происходит слишком поздно - только тогда, когда выполнение переходит в неуправляемый код, и к этому времени большое количество внутреннего кода .NET может уже выполняться в «неправильном» потоке - коде, который не является поточно-ориентированным.

0 голосов
/ 30 ноября 2009

Основная причина в том, что текстовое поле не принадлежит фоновому потоку.

Ваш поток пользовательского интерфейса владеет всеми объектами пользовательского интерфейса, и вы нажимаете на фоновый поток при нажатии кнопки. Этот фоновый поток не должен иметь доступа ни к каким объектам пользовательского интерфейса.

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

Посмотрите здесь объяснение (и решение).

...