Простое программирование потоков - PullRequest
3 голосов
/ 06 мая 2010

Я начал играть с потоками в c #, но теперь мне нужна помощь, вот мой код:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();

        DoCount();
    }
    public void DoCount()
    {
        for (int i = 0; i < 100; i++)
        {
            objTextBox.Text = i.ToString();
            Thread.Sleep(100);
        }
    }
}

это простые формы выигрыша с текстовым полем, я хочу увидеть "счет", но, как вы видите в моем коде, текстовое поле показывает мне 99, оно считается до 99, а затем появляется ... я думаю Я должен управлять этим в новой теме, но не знаю как!

Ответы [ 9 ]

8 голосов
/ 06 мая 2010

Используйте BackgroundWorker . На MSDN есть обзор BackgroundWorker .

Вот пример того, как может выглядеть ваш код:

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

    private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
    {
        BackgroundWorker backgroundWorker = (BackgroundWorker)sender;
        for (int i = 0; i < 100; i++)
        {
            backgroundWorker.ReportProgress(i);
            Thread.Sleep(100);
        }
    }

    private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        textBox1.Text = e.ProgressPercentage.ToString();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        backgroundWorker1.RunWorkerAsync();
    }
}

Другие примечания:

  • Не забудьте установить WorkerReportsProgress в конструкторе, если вы хотите, чтобы прогресс работал.
  • При использовании BackgroundWorker также часто полезно использовать элемент управления ProgressBar .
  • Если вы хотите, чтобы иметь возможность отменить фоновый работник, это тоже возможно. См. CancelAsync и WorkerSupportsCancellation .
  • Когда фоновый работник завершает работу, он запускает событие RunWorkerCompleted .
7 голосов
/ 06 мая 2010

Это может быть то, что вы ищете:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
        DoCount();
    }
    public void DoCount()
    {
        Thread t = new Thread(new ThreadStart(delegate
        {
           for (int i = 0; i < 100; i++)
           {
               this.Invoke((Action) delegate { objTextBox.Text = i.ToString(); });
               Thread.Sleep(1000);
           }
        }));
        t.IsBackground = true;
        t.Start();
    }
}

Примечания

  1. Использует базовую тему , а не BackgroundWorker
  2. Использует Вызвать для обновления текстового поля в потоке пользовательского интерфейса
  3. Устанавливает IsBackground в true, поэтому программа завершается, если форма закрывается до завершения цикла.
1 голос
/ 06 мая 2010

Вам вообще не нужен поток, чтобы делать подобные вещи - рассмотрите возможность изменения кода для обработки событий и используйте объект System.Windows.Forms.Timer для реализации ваших временных параметров. Использование таймеров для этого имеет огромное преимущество - оно не стоит 1 МБ памяти (как поток), и вам не нужно синхронизировать их - Windows сделает это за вас.

1 голос
/ 06 мая 2010

Вы можете попробовать SynchronizationContext , чтобы сделать это.

Вот небольшой пример, который я недавно отбросил:

public partial class Form1 : Form
{
    private SynchronizationContext c;
    private Thread t;
    private EventWaitHandle pause =
        new EventWaitHandle(false, EventResetMode.ManualReset);

    public Form1()
    {
        this.InitializeComponent();
        this.c = SynchronizationContext.Current;
    }

    private void Form1Activated(object sender, EventArgs e)
    {
        this.t = new Thread(new ThreadStart(delegate
        {
            this.pause.Reset();
            while (this.t.IsAlive && !this.pause.WaitOne(1000))
            {
                this.c.Post(
                    state => this.label1.Text = DateTime.Now.ToString(),
                    null);
            }
        }));
        this.t.IsBackground = true;
        this.t.Start();
    }

    private void Form1Deactivate(object sender, EventArgs e)
    {
        this.pause.Set();
        this.t.Join();
    }

    /// <summary>
    /// Button1s the click.
    /// </summary>
    /// <param name="sender">The sender.</param>
    /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
    private void Button1Click(object sender, EventArgs e)
    {
        this.Close();
    }
}
0 голосов
/ 07 мая 2010

Вот мой пример:

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

    private void Form1_Load(object sender, EventArgs e)
    {
        Action countUp = this.CountUp;
        countUp.BeginInvoke(null, null);
    }

    private void CountUp()
    {
        for (int i = 0; i < 100; i++)
        {
            this.Invoke(new Action<string>(UpdateTextBox), new object[] { i.ToString() });
            Thread.Sleep(100);
        }
    }

    private void UpdateTextBox(string text)
    {
        this.textBox1.Text = text;
    }
}
0 голосов
/ 06 мая 2010

Многопоточность может решить эту проблему, но для чего-то такого простого, как этот счетчик, это не нужно.

Другой пользователь рекомендовал это. Обновление (). Это работает для отображения чисел, потому что пользовательский интерфейс будет перерисовываться. Но это не учитывает тот факт, что окно не реагирует (вы не можете перемещать его).

Третье решение и моя рекомендация для этой конкретной программы - Application.DoEvents (). Что он делает, так это говорит базовому собственному окну выполнить его метод ProcessMessages в пуле сообщений. Пул сообщений содержит сообщения о событиях, отправленные ему Windows, когда необходимо перерисовать окно, переместить мышь, форма была перемещена, свернута и т. Д. Эти инструкции были отправлены Windows и поставлены в очередь. Проблема в том, что программа не будет обрабатывать их, пока пользовательский интерфейс не будет работать. Вы можете принудительно вызвать его, вызвав этот метод.

Application.DoEvents () выдаст окно, которое отвечает, как ожидается, с интервалами в 100 мс. Это может быть немного изменчиво (потоки были бы более отзывчивыми), но его очень легко вставить и часто достаточно.

for (int i = 0; i < 100; i++)
{
    objTextBox.Text = i.ToString();
    Application.DoEvents();
    Thread.Sleep(100);
}
0 голосов
/ 06 мая 2010

Мое решение практически такое же, как у Марка.Единственное отличие - я проверяю InvokeRequired в моем событии ProgressChangedВот мой пример кода:

using System.ComponentModel;
using System.Threading;
using System.Windows.Forms;

namespace tester
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            if (!backgroundWorker1.IsBusy)
                backgroundWorker1.RunWorkerAsync();
        }

        /// <summary>
        /// This delegate enables asynchronous calls for setting the text property on a control.
        /// </summary>
        delegate void SetTextCallback(string status);

        private void BackgroundWorker1DoWork(object sender, DoWorkEventArgs e)
        {
            for (var i = 0; i < 100; i++)
            {
                backgroundWorker1.ReportProgress(i);
                Thread.Sleep(100);
            }
        }

        private void BackgroundWorker1ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            if (label1.InvokeRequired)
                Invoke(new SetTextCallback(SetLabelText), new object[] { e.ProgressPercentage.ToString()});
            else
                SetLabelText(e.ProgressPercentage.ToString());
        }

        private void SetLabelText(string text)
        {
            label1.Text = text;
        }
    }
}
0 голосов
/ 06 мая 2010

После Thread.Sleep, попробуйте это:

this.Update();
0 голосов
/ 06 мая 2010

Не звоните DoCount напрямую, звоните ThreadPool.QueueUserWorkItem(DoCount). Это запустит DoCount в новом потоке. (Есть и другие способы, которые также работают, но это самый простой, который работает хорошо.)

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

Для решения этой проблемы используйте код, подобный следующему:

if (this.textBox1.InvokeRequired)
{   
    SetTextCallback d = new SetTextCallback(SetText);
    this.Invoke(d, new object[] { text });
}

См. http://msdn.microsoft.com/en-us/library/ms171728(VS.80).aspx для полного примера.

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