如何在新线程中运行一小段代码?

时间:2008-12-12 16:53:49

标签: c# .net multithreading

我有一些代码需要在与GUI不同的线程中运行,因为它当前导致表单在代码运行时冻结(10秒左右)。

假设我以前从未创建过新的帖子;什么是如何在C#中使用.NET Framework 2.0或更高版本执行此操作的简单/基本示例?

15 个答案:

答案 0 :(得分:304)

开始阅读的好地方是Joe Albahari

如果你想创建自己的线程,这很简单:

using System.Threading;
new Thread(() => 
{
    Thread.CurrentThread.IsBackground = true; 
    /* run your code here */ 
    Console.WriteLine("Hello, world"); 
}).Start();

答案 1 :(得分:190)

BackgroundWorker似乎是您的最佳选择。

这是我的最小例子。单击按钮后,后台工作程序将开始在后台线程中工作,并同时报告其进度。它还将在工作完成后报告。

using System.ComponentModel;
...
    private void button1_Click(object sender, EventArgs e)
    {
        BackgroundWorker bw = new BackgroundWorker();

        // this allows our worker to report progress during work
        bw.WorkerReportsProgress = true;

        // what to do in the background thread
        bw.DoWork += new DoWorkEventHandler(
        delegate(object o, DoWorkEventArgs args)
        {
            BackgroundWorker b = o as BackgroundWorker;

            // do some simple processing for 10 seconds
            for (int i = 1; i <= 10; i++)
            {
                // report the progress in percent
                b.ReportProgress(i * 10);
                Thread.Sleep(1000);
            }

        });

        // what to do when progress changed (update the progress bar for example)
        bw.ProgressChanged += new ProgressChangedEventHandler(
        delegate(object o, ProgressChangedEventArgs args)
        {
            label1.Text = string.Format("{0}% Completed", args.ProgressPercentage);
        });

        // what to do when worker completes its task (notify the user)
        bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(
        delegate(object o, RunWorkerCompletedEventArgs args)
        {
            label1.Text = "Finished!";
        });

        bw.RunWorkerAsync();
    }

注意:

  • 我把所有东西都放在一个方法中 使用C#的匿名方法 简单,但你总是可以拉 他们用不同的方法。
  • 在其中更新GUI是安全的 ProgressChangedRunWorkerCompleted处理程序。 但是,从DoWork更新GUI 会引发 InvalidOperationException

答案 2 :(得分:96)

ThreadPool.QueueUserWorkItem非常适合简单的事情。唯一需要注意的是从另一个线程访问控件。

System.Threading.ThreadPool.QueueUserWorkItem(delegate {
    DoSomethingThatDoesntInvolveAControl();
}, null);

答案 3 :(得分:71)

快速又脏,但它会起作用:

在顶部使用:

using System.Threading;

简单代码:

static void Main( string[] args )
{
    Thread t = new Thread( NewThread );
    t.Start();
}

static void NewThread()
{
    //code goes here
}

我只是把它扔进了一个新的控制台应用程序,用于一个例子

答案 4 :(得分:59)

这是另一种选择:

Task.Run(()=>{
//Here is a new thread
});

答案 5 :(得分:40)

尝试使用BackgroundWorker课程。您可以为代理人提供运行的内容,并在工作完成时收到通知。我链接到的MSDN页面上有一个示例。

答案 6 :(得分:14)

如果你想得到一个值:

var someValue;

Thread thread = new Thread(delegate()
            {                 
                //Do somthing and set your value
                someValue = "Hello World";
            });

thread.Start();

while (thread.IsAlive)
  Application.DoEvents();

答案 7 :(得分:6)

将该代码放在一个函数中(无法在与GUI相同的线程上执行的代码),并触发该代码的执行,如下所示。

Thread myThread= new Thread(nameOfFunction);

workerThread.Start();

调用线程对象上的start函数将导致在新线程中执行函数调用。

答案 8 :(得分:4)

这里如何使用带有progressBar的线程,它只是用于了解线程的工作方式,在表单中有三个progressBar和4按钮:

 public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }
    Thread t, t2, t3;
    private void Form1_Load(object sender, EventArgs e)
    {

        CheckForIllegalCrossThreadCalls = false;

         t = new Thread(birinicBar); //evry thread workes with a new progressBar


         t2 = new Thread(ikinciBar);


         t3 = new Thread(ucuncuBar);

    }

    public void birinicBar() //to make progressBar work
    {
        for (int i = 0; i < 100; i++) {
            progressBar1.Value++;
            Thread.Sleep(100); // this progressBar gonna work faster
        }
    }

    public void ikinciBar()
    {
        for (int i = 0; i < 100; i++)
        {
            progressBar2.Value++;
            Thread.Sleep(200);
        }


    }

    public void ucuncuBar()
    {
        for (int i = 0; i < 100; i++)
        {
            progressBar3.Value++;
            Thread.Sleep(300);
        }
    }

    private void button1_Click(object sender, EventArgs e) //that button to start the threads
    {
        t.Start();
        t2.Start(); t3.Start();

    }

    private void button4_Click(object sender, EventArgs e)//that button to stup the threads with the progressBar
    {
        t.Suspend();
        t2.Suspend();
        t3.Suspend();
    }

    private void button2_Click(object sender, EventArgs e)// that is for contuniue after stuping
    {
        t.Resume();
        t2.Resume();
        t3.Resume();
    }

    private void button3_Click(object sender, EventArgs e) // finally with that button you can remove all of the threads
    {
        t.Abort();
        t2.Abort();
        t3.Abort();
    }
}

答案 9 :(得分:3)

// following declaration of delegate ,,,
public delegate long GetEnergyUsageDelegate(DateTime lastRunTime, 
                                            DateTime procDateTime);

// following inside of some client method
GetEnergyUsageDelegate nrgDel = GetEnergyUsage;
IAsyncResult aR = nrgDel.BeginInvoke(lastRunTime, procDT, null, null);
while (!aR.IsCompleted) Thread.Sleep(500);
int usageCnt = nrgDel.EndInvoke(aR);

查尔斯你的代码(上图)不正确。你不需要旋转等待完成。 EndInvoke将阻塞,直到发出WaitHandle信号。

如果你想阻止直到完成,你只需要

nrgDel.EndInvoke(nrgDel.BeginInvoke(lastRuntime,procDT,null,null));

或者

ar.AsyncWaitHandle.WaitOne();

但是如果阻止发出anyc呼叫有什么意义呢?您也可以使用同步调用。更好的选择是不阻止并传入lambda进行清理:

nrgDel.BeginInvoke(lastRuntime,procDT,(ar)=> {ar.EndInvoke(ar);},null);

要记住的一件事是必须调用EndInvoke。很多人都忘记了这一点并最终泄漏了WaitHandle,因为大多数异步实现在EndInvoke中释放了waithandle。

答案 10 :(得分:2)

如果您要使用原始Thread对象,则需要至少将IsBackground设置为true,并且还应设置Threading Apartment模型(可能是STA)。

public static void DoWork()
{
    // do some work
}

public static void StartWorker()
{
    Thread worker = new Thread(DoWork);
    worker.IsBackground = true;
    worker.SetApartmentState(System.Threading.ApartmentState.STA);
    worker.Start()
}

如果您需要UI交互,我会推荐BackgroundWorker类。

答案 11 :(得分:1)

另一个选项,它使用委托和线程池......

假设'GetEnergyUsage'是一个将DateTime和另一个DateTime作为输入参数的方法,并返回一个Int ...

// following declaration of delegate ,,,
public delegate long GetEnergyUsageDelegate(DateTime lastRunTime, 
                                            DateTime procDateTime);

// following inside of some client method 
GetEnergyUsageDelegate nrgDel = GetEnergyUsage;                     
IAsyncResult aR = nrgDel.BeginInvoke(lastRunTime, procDT, null, null);
while (!aR.IsCompleted) Thread.Sleep(500);
int usageCnt = nrgDel.EndInvoke(aR);

答案 12 :(得分:1)

在.Net中运行单独的线程有很多种方法,每种方法都有不同的行为。 GUI退出后是否需要继续运行线程?你需要在线程和GUI之间传递信息吗?线程是否需要更新GUI?该线程应该执行一项任务然后退出,还是应该继续运行?这些问题的答案将告诉您使用哪种方法。

Code Project网站上有一个很好的async method article,它描述了各种方法并提供了示例代码。

请注意,本文是在async/await模式之前编写的,而Task Parallel Library是在.NET中引入的。

答案 13 :(得分:0)

How to: Use a Background Thread to Search for Files

您必须非常小心地从其他线程访问GUI特定的东西(这对许多GUI工具包来说很常见)。如果你想在GUI中更新一些东西来处理我认为对WinForms有用的线程检查this answer。对于WPF,请参阅this(它显示了如何在UpdateProgress()方法中触摸组件,以便它可以在其他线程中运行,但实际上我不喜欢它在执行{{1}之前没有执行CheckAccess()通过Dispathcer,see并在其中搜索CheckAccess)

正在查看有关线程的.NET特定书籍并找到了this one(可免费下载)。有关详细信息,请参阅http://www.albahari.com/threading/

我相信你会发现你需要在前20页中以新线程的形式启动执行它还有更多(不确定GUI特定的片段,我的意思是严格特定于线程)。我很高兴听到社区对这项工作的看法,因为我正在读这篇文章。现在看起来非常整洁(用于显示特定于.NET的方法和线程类型)。它还涵盖了我真正欣赏的.NET 2.0(而不是古老的1.1)。

答案 14 :(得分:0)

我建议查看Jeff Richter的Power Threading Library,特别是IAsyncEnumerator。请查看Charlie Calvert's博客上的视频,其中Richter会对其进行详细介绍。

不要被名称拖延,因为它使异步编程任务更容易编码。