简单的线程编程

时间:2010-05-06 19:27:38

标签: c# multithreading winforms

我已经开始在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 个答案:

答案 0 :(得分:8)

使用BackgroundWorker。 MSDN上有一个BackgroundWorker overview

以下是代码外观的示例:

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

其他说明:

答案 1 :(得分:7)

这可能就是你要找的东西:

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. 使用调用更新UI线程上的文本框
  3. IsBackground 设置为true,以便在循环完成之前关闭表单时退出程序。

答案 2 :(得分:1)

您可能需要尝试使用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();
    }
}

答案 3 :(得分:1)

您根本不需要线程来执行此类操作 - 请考虑将代码更改为事件驱动并使用System.Windows.Forms.Timer对象来实现您的计时。使用计时器有一个巨大的优势 - 它不需要1MB内存(一个线程),而且你不需要同步它们 - windows会为你做。

答案 4 :(得分:0)

请勿直接致电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

答案 5 :(得分:0)

在Thread.Sleep之后,试试这个:

this.Update();

答案 6 :(得分:0)

我的解决方案与马克的解决方案几乎相同。唯一的区别是我在ProgressChanged事件中检查InvokeRequired。这是我的示例代码:

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

答案 7 :(得分:0)

多线程可以解决这个问题,但对于像这个计数器这样简单的东西,它是不必要的。

另一位用户推荐了this.Update()。这可以使数字显示,因为UI将重绘自身。但它没有解决窗口没有响应的事实(你无法移动它)。

第三个解决方案和我对这个特定程序的建议是Application.DoEvents()。这样做是告诉底层本机窗口在消息池上执行其ProcessMessages方法。消息池包含Windows在需要重新绘制窗口,移动鼠标,移动窗体,最小化等窗口时发送给它的事件消息。这些指令由Windows发送并已排队。问题是程序在UI空闲之前不会处理它们。您可以通过调用此方法强制它。

Application.DoEvents()将产生一个窗口,该窗口以100 ms的间隔响应预期。它可能有点不稳定(线程会更敏感)但是它很容易放入并且通常就足够了。

for (int i = 0; i < 100; i++)
{
    objTextBox.Text = i.ToString();
    Application.DoEvents();
    Thread.Sleep(100);
}

答案 8 :(得分:0)

以下是一个简单示例:

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