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

假设我以前从未创建过新线程;在C#中使用.NET Framework 2.0或更高版本的简单/基本示例是什么?

评论

当时大多数答案都是不错的,但是.NET Framework 4.0的改进使事情变得更简单。您可以使用Task.Run()方法,如以下答案中所述:stackoverflow.com/a/31778592/1633949

#1 楼

乔·阿尔巴哈里(Joe Albahari)是开始阅读的好地方。

如果您要创建自己的线程,就这么简单:

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


评论


@EdPower,这仅适用于Winforms ..还是可以在Web Forms ..中使用?

–MethodMan
16年1月28日,下午3:31

@MethodMan-是的,它将在Web窗体中工作。从这里开始:

– Ed Power
16 Jan 28'在19:11



将IsBackground设置为true时要小心。它可能没有按照您的想法做。它的作用是配置在所有前台线程都死亡后是否杀死该线程,或者该线程是否使应用程序保持活动状态。如果您不希望线程在执行过程中终止,则不要将IsBackground设置为true。

–Zero3
16-09-22在13:34



@EdPower我认为您都应该小心!您可能不希望在执行过程中终止的一项任务示例是将数据保存到磁盘的示例。但是可以肯定的是,如果您的任务适合随时终止,则该标志很好。我的意思是,由于您没有描述标志的用途,因此在使用该标志时需要格外小心,并且它的命名很容易使人相信它所做的事与实际不同。

–Zero3
16-09-23在8:44



使用.NET Framework 4.0+,只需使用Task.Run(),如此答案中所述:stackoverflow.com/a/31778592/1633949

–理查德二世
19-10-25在13:58

#2 楼

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#的匿名方法实现简单性,但您始终可以将它们拉出使用不同的方法。

ProgressChanged
RunWorkerCompleted处理程序中更新GUI是安全的。 br />但是,从DoWork更新GUI InvalidOperationException


评论


使用System.ComponentModel; (可能会帮助人们避免执行Google搜索,感谢您提供此有用的代码示例)+1

–惊喜
2011年3月7日在19:04

@Gant感谢您提供示例代码。当我尝试您的代码时,ProgressChanged事件未触发。但是,当我在这里尝试类似的可接受的答案时[stackoverflow.com/questions/23112676/…,它起作用了。

–马丁
2014年9月4日在12:47



@sooprise Ctrl +。在这些情况下可以为您提供帮助! (假设您使用的是Visual Studio)

– SepehrM
2014年11月13日在18:20

#3 楼

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

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


评论


也是我的最爱。一条快速路口。我将其放在快速拨号(摘要)上。与控件一起很好地工作。您只需要知道如何使用Invoke和InvokeRequired。

– Bitterblue
2014年7月8日在11:34

+1请注意,委托还允许您整齐地包装参数。

–威尔·比克福德
2014年8月21日在19:17

如何将ThreadPool.QueueUserWorkItem与回调函数一起使用意味着工作完成后,回调将通知我们。

–牟
17-2-22在12:27

#4 楼

快速又脏,但是可以使用:

在顶部使用:

using System.Threading;


简单代码:

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

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


我只是把它扔到了新的控制台应用程序中,例如

评论


请记住,标记为IsBackground的线程不会在运行时自动终止。这需要通过应用程序进行线程管理。如果将线程标记为非背景,则在线程执行后,该线程将为您终止。

– CmdrTallen
09年3月16日在14:29

@CmdrTallen:不太正确。标记为IsBackground = true的线程表示该线程不会停止进程退出,即,当所有具有IsBackground = false的线程退出时,进程将退出

–菲尔·德瓦尼(Phil Devaney)
2009年8月6日15:42

#5 楼

这是另一个选择:

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


评论


如果以这种方式创建线程,您如何从UI线程中停止该线程?

–slayernoah
16年5月6日在17:44

@slayernoah,您可以将CancellationTokenSource作为参数传递给它,并在线程外设置取消令牌:msdn.microsoft.com/en-us/library/dd997364(v=vs.110).aspx

–海绵宝宝同志
16年5月9日,2:35

此代码不能保证您拥有新线程。该决定取决于您正在使用的ThreadPool的当前实现。参见示例stackoverflow.com/questions/13570579/…

–朱利奥·卡钦(Giulio Caccin)
17年9月5日在12:51

@GiulioCaccin ThreadPool可以从其池中选择一个现有线程,但这绝对是一个不同的线程。

–海绵宝宝同志
17年9月18日在3:58

#6 楼

尝试使用BackgroundWorker类。您为它提供了运行什么的委托,并在工作完成时得到通知。我链接到的MSDN页面上有一个示例。

评论


您当然可以使用Thread类执行此操作,但是BackgroundWorker为您提供了用于完成线程和进行进度报告的方法,否则您将不得不弄清楚如何使用自己。不要忘记,您必须使用Invoke与UI对话!

–罗伯特·罗斯尼(Robert Rossney)
08年12月12日在18:49

请注意:BackgroundWorker有一些细微的限制,但是对于常见的“我想离开去做某事并使我的表单保持响应状态”,这真是太棒了。

–梅鲁斯
09年1月28日在0:49

@Merus,您能否扩展一下这些细微的限制?

–克里斯蒂安·休敦
2012年9月18日在20:34

#7 楼

如果要获取值:

var someValue;

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

thread.Start();

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


#8 楼

将该代码放入一个函数(无法在与GUI相同的线程上执行的代码)中,然后触发以下代码来触发该代码的执行。

Thread myThread= new Thread(nameOfFunction);

workerThread.Start();

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

评论


@ user50612您在说什么?新线程将在后台运行nameOfFunction,而不是在当前GUI线程上运行。 IsBackground属性确定线程是否将使应用程序保持活动状态:msdn.microsoft.com/en-us/library/…

–Zero3
16-09-22在13:29



#9 楼

这里如何将线程与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();
    }
}


#10 楼

// 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));


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

ar.AsyncWaitHandle.WaitOne();


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

#11 楼

如果要使用原始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类。

评论


将IsBackground设置为true时要小心。它可能没有按照您的想法做。它的作用是配置在所有前台线程都死亡后是否杀死该线程,或者该线程是否使应用程序保持活动状态。如果您不希望线程在执行过程中终止,则不要将IsBackground设置为true。

–Zero3
16-09-22在13:35

文件:msdn.microsoft.com/en-us/library/…

–Zero3
16-09-22在13:36

#12 楼

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

假设“ 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);


评论


查尔斯,您的代码在功能上与int usageCnt = nrgDel.Invoke(lastRunTime,procDT,null,null); ?看起来无论如何它都会暂停当前线程的睡眠...我想如果在GUI线程中调用它对GUI冻结没有任何帮助

– IgorK
08年12月12日在17:45

是的,BeginInvoke在线程池中的另一个线程上调用委托...如果使用Invoke,则在当前线程上同步调用委托...但是您是对的,睡眠是错误的...您应该消除这种情况,并使用回调函数收集结果。我将进行编辑以显示

–查尔斯·布雷塔纳(Charles Bretana)
08-12-12在17:54

#13 楼

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

Code Project网站上有一篇很好的异步方法文章,描述了各种方法并提供了示例代码。

请注意,本文是在将异步/等待模式和任务并行库引入.NET之前写的。

评论


这是我一直在寻找的信息类型,但是您的答案是link rot的受害者。

–克里斯
18 Mar 26 '18在16:46

感谢您让我知道,@ Chris;链接固定。

–杜高拱门
18 Mar 26 '18在16:54

#14 楼

如何:使用后台线程搜索文件

您必须非常小心,其他线程对GUI特定内容的访问(在许多GUI工具包中都很常见)。如果要从处理线程更新GUI中的某些内容,请检查此答案,我认为该答案对WinForms有用。对于WPF,请参见此文件(它显示了如何触摸UpdateProgress()方法中的组件,以便它可以在其他线程中工作,但是实际上我不希望它在通过Dispathcer执行CheckAccess()之前没有执行BeginInvoke,请在其中查找和搜索CheckAccess)

在查找有关.NET的特定于线程的书时,发现了这一本书(可免费下载)。有关它的更多详细信息,请参见http://www.albahari.com/threading/。

我相信您会在前20页中找到以新线程的身份启动执行所需的内容。 (不确定GUI特定的片段,我的意思是严格地特定于线程)。听到社区对这项工作的看法感到很高兴,因为我正在读这本书。现在对我来说看起来很整洁(用于显示.NET特定于线程的方法和类型)。它还涵盖了我真正欣赏的.NET 2.0(而不是古老的1.1)。

#15 楼

我建议您查看Jeff Richter的Power Threading Library,尤其是IAsyncEnumerator。请看一下Charlie Calvert博客上的视频,其中Richter对其进行了很好的概述。

别忘了这个名称,因为它使异步编程任务更易于编码。