如何开启一个线程获取线程方法的返回值
我们首先可以使用委托开启线程
//定义我们的线程方法
static int Test(int i,string str)
{
Console.WriteLine("test"+i+str);
Thread.Sleep(100);//让当前线程休眠(暂停线程的执行) 单位ms
return 100;
}
在Main方法里面通过BeginInvoke开启线程,因为有些线程方法比较耗时,所以这里我们可以使用异步返回的方式检测这个线程方法是否执行完毕
static void Main(string[] args) {
Func<int, string, int> a = Test;
IAsyncResult ar = a.BeginInvoke(100, "siki", null, null);// 开启一个新的线程去执行 a所引用的方法
IAsyncResult 可以取得当前线程的状态
可以认为线程是同时执行的(异步执行)
Console.WriteLine("main");
while (ar.IsCompleted == false)//如果当前线程没有执行完毕
{
Console.Write(".");
Thread.Sleep(10); //控制子线程的检测频率
}
int res = a.EndInvoke(ar);//取得异步线程的返回值
Console.WriteLine(res);
}
当然除了异步检测,我们同样可以使用内置方法给定这个线程执行事件
通常我们会使用这个方法检测网络连接
bool isEnd = ar.AsyncWaitHandle.WaitOne(1000);//1000毫秒表示超时时间,如果等待了1000毫秒 线程还没有结束的话 那么这个方法会返回false 如果在1000毫秒以内线程结束了,那么这个方法会返回true
if (isEnd)
{
int res = a.EndInvoke(ar);
Console.WriteLine(res);
}
通过回调检测线程结束
//定义我们回调函数需要执行的方法,主要进行一些判断用来终止回调
static void OnCallBack( IAsyncResult ar )
{
Func<int, string, int> a = ar.AsyncState as Func<int, string, int>;
int res = a.EndInvoke(ar);
Console.WriteLine(res+"在回调函数中取得结果");
}
//调用委托开启线程
Func<int, string, int> a = Test;
//倒数第二个参数是一个委托类型的参数,表示回调函数,就是当线程结束的时候会调用这个委托指向的方法 倒数第一个参数用来给回调函数传递数据,这里就是将这个正在执行的线程方法传递过去
IAsyncResult ar = a.BeginInvoke(100, "siki", OnCallBack, a);// 开启一个新的线程去执行 a所引用的方法
当然我们既然可以写回调方法,那么我们就可以使用Lambda表达式书写这个方法
a.BeginInvoke(100, "siki", ar =>
{
int res = a.EndInvoke(ar);
Console.WriteLine(res+"在lambda表达式中取得");
}, null);
通过Thread类开启线程(常用方法)
//写一个我们的线程方法,注意如果是带有参数的话必须要使用Object类型
//Thread.CurrentThread.ManagedThreadId这个东西是用来获取当前线程的ID,以后可以通过ID做管理
static void DownloadFile(object filename)
{
Console.WriteLine("开始下载:" +Thread.CurrentThread.ManagedThreadId +filename);
Thread.Sleep(2000);
Console.WriteLine("下载完成");
}
下面使Thread方法开启线程执行这个方法
可以看到我们在实例化Thread的时候就是在开启线程,然后通过start方法执行这个线程,假如这个方法带有参数的话,就直接在start开启的时候直接传递参数就行了
static void Main(string[] args) {
Thread t = new Thread(DownloadFile);//创建出来Thread对象,这个线程并没有启动
t.Start("xxx.种子");//开始,开始去执行线程
Console.WriteLine("Main");
}
同样的我们将线程方法传入Thread里面说明这个构造函数本质也是一个委托,既然这样我们就可以使用Lambda表达式构造
Thread t = new Thread(() =>
{
Console.WriteLine("开始下载:" + Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(2000);
Console.WriteLine("下载完成");
});
t.Start();
我们还可以通过对象进行开启线程
首先我们创建一个类
class MyThread
{
private string filename;
private string filepath;
public MyThread(string fileName, string filePath)
{
this.filename = fileName;
this.filepath = filePath;
}
public void DownFile()
{
Console.WriteLine("开始下载"+filepath+filename);
Thread.Sleep(2000);
Console.WriteLine("下载完成");
}
}
然后我们再main方法里面给这个类实例化并且用这个实例化的类开启我们的线程,和直接绑定方法类似
MyThread my = new MyThread("xxx.bt", "http://www.xxx.bbs");//实例化
Thread t = new Thread(my.DownFile);//我们构造一个thread对象的时候,可以传递一个静态方法,也可以传递一个对象的普通方法
t.Start();
前台线程和后台线程
前台线程就是我们通过Thread类开启的线程,后台线程就是通过线程池开启的线程,使用区别不大,但是后台线程会在前台线程全部执行完毕后被立即杀死,结束当前程序的运行
Thread t = new Thread(DownloadFile);//这个是前台线程
t.IsBackground = true;//设置为后台线程
线程池
使用线程池需要注意的事项:
线程池中的所有线程都是后台线程 。 如果进程的所有前台线程都结束了,所有的后台线程就会停止。 不能把入池的线程改为前台线程 。
不能给入池的线程设置优先级或名称。
入池的线程只能用于时间较短的任务。 如果线程要一直运行(如 Word的拼写检查器线程),就应使用Thread类创建一个线程。
static void ThreadMethod(object state)
{
Console.WriteLine("线程开始:"+Thread.CurrentThread.ManagedThreadId);//获取当前线程ID
Thread.Sleep(2000);
Console.WriteLine("线程结束");
}
static void Main(string[] args)
{
ThreadPool.QueueUserWorkItem(ThreadMethod);//开启一个工作线程
ThreadPool.QueueUserWorkItem(ThreadMethod);//开启一个工作线程
ThreadPool.QueueUserWorkItem(ThreadMethod);//开启一个工作线程
ThreadPool.QueueUserWorkItem(ThreadMethod);//开启一个工作线程
ThreadPool.QueueUserWorkItem(ThreadMethod);//开启一个工作线程
ThreadPool.QueueUserWorkItem(ThreadMethod);//开启一个工作线程
ThreadPool.QueueUserWorkItem(ThreadMethod);//开启一个工作线程
Console.ReadKey();//必须要有,不然当主线程结束后这些线程池的后台线程会被立即杀死
}
通过任务开启一个线程
static void ThreadMethod() {
Console.WriteLine("任务开始");
Thread.Sleep(2000);
Console.WriteLine("任务结束");
}
static void Main(string[] args) {
//第一种,使用Task创建
//Task t = new Task(ThreadMethod);//传递一个需要线程去执行的方法
//t.Start();
//第二种,使用TaskFactory
TaskFactory tf = new TaskFactory();
Task t = tf.StartNew(ThreadMethod);//这里使用了工厂模式,可以通过tf一直开启线程
Console.WriteLine("Main");
Console.ReadKey();
}
注意,使用Task可以开启连续任务,将这些人物串联起来
static void DoFirst(){
Console.WriteLine("do in task : "+Task.CurrentId);
Thread.Sleep(3000);
}
static void DoSecond(Task t){
Console.WriteLine("task "+t.Id+" finished.");
Console.WriteLine("this task id is "+Task.CurrentId);
Thread.Sleep(3000);
}
Task t1 = new Task(DoFirst);
Task t2 = t1.ContinueWith(DoSecond);
Task t3 = t1.ContinueWith(DoSecond);
Task t4 = t2.ContinueWith(DoSecond);
通过ContinueWith这个方法我们可以给这些任务创建连接,这个例子就是,当ti的任务执行完成同时执行t2和t3,当t2执行完成才会执行t4;