Answers:
Joe Albahari是开始阅读的好地方。
如果要创建自己的线程,这很简单:
using System.Threading;
new Thread(() =>
{
Thread.CurrentThread.IsBackground = true;
/* run your code here */
Console.WriteLine("Hello, world");
}).Start();
IsBackground
为true时要小心。它可能没有按照您的想法做。它的作用是配置在所有前台线程都死亡后是否杀死该线程,或者该线程是否使应用程序保持活动状态。如果您不希望线程在执行过程中终止,则不要设置IsBackground
为true。
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();
}
注意:
ProgressChanged
或
RunWorkerCompleted
处理程序中更新GUI是安全的
。但是,从更新GUI DoWork
将导致
InvalidOperationException
。该ThreadPool.QueueUserWorkItem是相当理想的简单的东西。唯一的警告是从另一个线程访问控件。
System.Threading.ThreadPool.QueueUserWorkItem(delegate {
DoSomethingThatDoesntInvolveAControl();
}, null);
快速又肮脏,但是可以使用:
在顶部使用:
using System.Threading;
简单的代码:
static void Main( string[] args )
{
Thread t = new Thread( NewThread );
t.Start();
}
static void NewThread()
{
//code goes here
}
我只是把它扔到一个新的控制台应用程序中
这是另一个选择:
Task.Run(()=>{
//Here is a new thread
});
尝试使用BackgroundWorker类。您给它委托什么运行,并在工作完成时得到通知。我链接到的MSDN页面上有一个示例。
将该代码放入一个函数(不能在与GUI相同的线程上执行的代码)中,然后触发以下代码以触发该代码的执行。
Thread myThread= new Thread(nameOfFunction);
workerThread.Start();
在线程对象上调用start函数将导致在新线程中执行函数调用。
nameOfFunction
在后台运行,而不是在当前GUI线程上运行。该IsBackground
属性决定线程是否保持应用程序活着与否:msdn.microsoft.com/en-us/library/...
在这里,如何将线程与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();
}
}
// 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。
如果要使用原始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类。
另一个选项,使用委托和线程池...
假设“ 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冻结没有任何帮助
在.Net中有很多运行单独线程的方法,每种方法都有不同的行为。GUI退出后是否需要继续运行线程?您是否需要在线程和GUI之间传递信息?线程是否需要更新GUI?线程应该先执行一项任务然后退出,还是应该继续运行?这些问题的答案将告诉您使用哪种方法。
Code Project网站上有一篇很好的异步方法文章,描述了各种方法并提供了示例代码。
从其他线程访问GUI特定内容(在许多GUI工具包中很常见),您必须非常谨慎。如果要从处理线程更新GUI中的某些内容,请检查此答案,我认为这对WinForms很有用。对于WPF看到这个(它显示了如何在的UpdateProgress触摸组件()方法,因此会从其他线程的工作,但实际上我不喜欢它是不是做CheckAccess()
在做之前BeginInvoke
通过Dispathcer,看在它搜索的checkAccess)
正在寻找有关.NET的特定于线程的书,并发现了这一本书(可免费下载)。有关它的更多详细信息,请参见http://www.albahari.com/threading/。
我相信您会在前20页中找到作为新线程启动执行所需的内容,并且还有更多内容(不确定特定于GUI的代码片段,我的意思是严格针对线程)。很高兴听到社区对这项工作有何看法,因为我正在阅读此书。现在对我来说看起来很整洁(用于显示.NET特定于线程的方法和类型)。它还涵盖了.NET 2.0(而不是古老的1.1),我对此非常感激。
我建议您查看Jeff Richter的Power Threading Library,尤其是IAsyncEnumerator。观看有关查理·卡尔维特(Charlie Calvert)的视频博客,Richter对其进行了详细介绍。
不要被这个名字拖延,因为它使异步编程任务更容易编码。