苏州个人网站制作,网站的外部链接建设图片,中国科技成就按时间顺序,上海建筑工程有限公司多线程的操作在程序中也是比较常见的#xff0c;比如开启一个线程执行一些比较耗时的操作(IO操作)#xff0c;而主线程继续执行当前操作#xff0c;不会造成主线程阻塞。线程又分为前台线程和后台线程#xff0c;区别是#xff1a;整个程序必须要运行完前台线程才会退出比如开启一个线程执行一些比较耗时的操作(IO操作)而主线程继续执行当前操作不会造成主线程阻塞。线程又分为前台线程和后台线程区别是整个程序必须要运行完前台线程才会退出而后台线程会在程序退出的时候结束掉。Thread默认创建的是前台线程而ThreadPool和Task默认创建的是后台线程Thread可以通过设置 IsBackground 属性将线程设置为后台线程。 static void Main(string[] args)
{Thread thread new Thread(new ThreadStart(NoParameterMethod));thread.Start();Console.WriteLine(程序已经执行完成);
}static void NoParameterMethod()
{Thread.Sleep(1000);Console.WriteLine(NoParameterMethod);
} 效果 static void Main(string[] args)
{Thread thread new Thread(new ThreadStart(NoParameterMethod)){IsBackground true};thread.Start();Console.WriteLine(程序已经执行完成);
}static void NoParameterMethod()
{Thread.Sleep(1000);Console.WriteLine(NoParameterMethod);
} 效果 下面来说一下几种开启多线程的方法
1、Thread
1.1 开启一个线程执行一个不带参数的方法 static void Main(string[] args)
{Thread thread new Thread(new ThreadStart(NoParameterMethod));//注意Start开启线程之后当前线程不是说一定会立马执行//而是说当前线程已经准备好被CPU调用至于CPU什么时候调用是需要看情况而定thread.Start();Console.WriteLine(程序已经执行完成);
}static void NoParameterMethod()
{//使当前线程停止1sThread.Sleep(1000);Console.WriteLine(NoParameterMethod);
} 1.2开启一个线程执行带参数的方法 static void Main(string[] args)
{Thread thread new Thread(new ParameterizedThreadStart(ParameterMethod));//要传入的参数在Start的时候传入thread.Start(ParameterMethod);Console.WriteLine(程序已经执行完成);
}
//参数类型必须为Object类型方法只能有一个参数
//如果想传入多个参数可已将参数封装进入一个类中
static void ParameterMethod(Object x) {Thread.Sleep(1000);Console.WriteLine(x);
} 2、ThreadPool
使用ThreadPool开启一个线程
//无参 Thread.CurrentThread.ManagedThreadId是当前线程的唯一标识符
ThreadPool.QueueUserWorkItem(new WaitCallback(obj Console.WriteLine(Thread.CurrentThread.ManagedThreadId)));
//有参
ThreadPool.QueueUserWorkItem(new WaitCallback(obj Console.WriteLine(Thread.CurrentThread.ManagedThreadId)), 参数);
ThreadPool是Thread的一个升级版ThreadPool是从线程池中获取线程如果线程池中又空闲的元素则直接调用如果没有才会创建而Thread则是会一直创建新的线程要知道开启一个线程就算什么事都不做也会消耗大约1m的内存是非常浪费性能的接下来我们写一个例子来看一下二者的区别 #region 使用Thread开启100个线程
for (int i 0; i 100; i)
{(new Thread(new ThreadStart(() Console.WriteLine(Thread.CurrentThread.ManagedThreadId)))).Start();
}
#endregion 运行结果 我们可以看到每一个主线程表示id都是不同的也就是说使用Thread开启线程每次都是新创建一个 #region 使用ThreadPool开启100个线程
for (int i 0; i 100; i)
{ThreadPool.QueueUserWorkItem(new WaitCallback(obj Console.WriteLine(Thread.CurrentThread.ManagedThreadId)));
}
#endregion运行结果 相信区别已经很明显了这里我再说一下线程池中一开始是没有一个线程的使用ThreadPool开启一个线程之后线程执行完毕会加入到线程池中后续需要再次开启线程的时候查看线程池中有没有空闲的线程有则调用没有则创建如此循环
二者之间还有一个区别就是ThreadPool可以操控线程的状态比如等待线程完成或者终止超时子线程操作
取消子线程操作
CancellationTokenSource cts new CancellationTokenSource();
ThreadPool.QueueUserWorkItem(new WaitCallback(CanCancelMethod),cts.Token);
cts.Cancel();
Console.ReadKey();static void CanCancelMethod(Object obj) {CancellationToken ct (CancellationToken)obj;if (ct.IsCancellationRequested) {Console.WriteLine(该线程已取消);}//就算ct.IsCancellationRequested为真接下来的代码还是会执行//因为该方法并没有ruturnThread.Sleep(1000);Console.WriteLine($子线程{Thread.CurrentThread.ManagedThreadId}结束);
} 感觉这个取消子线程的方法和设置一个全局变量然后通过判断和更改全局变量的值设置线程是否取消的效果一样 ThreadPool的其他操作感兴趣的可以自己搜索学一下因为终止线程什么操作都是比较麻烦的关于ThreadPool就不再多说了
3、Task
Task和ThreadPool是一样的都是从线程池中取空闲的线程 使用Task开启一个线程 //方法1 使用Task的Run方法
Task.Run(() {Console.WriteLine($线程{Thread.CurrentThread.ManagedThreadId}已开启);
});
//方法2 使用Task工厂类TaskFactory对象的StartNew方法
(new TaskFactory()).StartNew(()
{Console.WriteLine($线程{Thread.CurrentThread.ManagedThreadId}已开启);
}); Run和StartNew方法都是返回一个Task类型的对象代表当前开启的线程如果方法有返回值
//如果方法有返回值
Taskint t1 Task.Runint(() {return 1;
});
//通过t1.Result查看返回的结果
Console.WriteLine(t1.Result);
取消线程操作的话和ThreadPool取消线程操作一样 //1s后自动取消线程
CancellationTokenSource cts new CancellationTokenSource(1000);
//为取消线程注册回调函数
cts.Token.Register(() {Console.WriteLine(线程已取消);
});Task.Run(() {Console.WriteLine(开始执行);Thread.Sleep(2000);//判断当前线程是否已被取消if (cts.Token.IsCancellationRequested) {Console.WriteLine(方法已结束);return;}Console.WriteLine(线程继续执行);
},cts.Token); 等待所有线程执行完毕 //存放所有线程
ListTask lst new ListTask();
//开启10个线程
for (int i 0;i 10;i) {lst.Add(Task.Run(() {Thread.Sleep(new Random().Next(1,3000));Console.WriteLine($线程{Thread.CurrentThread.ManagedThreadId});}));
}
//等待所有线程执行完毕
Task.WaitAll(lst.ToArray());
Console.WriteLine(所有线程执行完毕); 等待任意一个先线程执行完毕 //存放所有线程
ListTask lst new ListTask();
//开启10个线程
for (int i 0;i 10;i) {lst.Add(Task.Run(() {Thread.Sleep(new Random().Next(1,3000));Console.WriteLine($线程{Thread.CurrentThread.ManagedThreadId});}));
}
//等待任意线程执行完毕
Task.WaitAny(lst.ToArray());
Console.WriteLine(已有现成执行完毕); 对于Thread、ThreadPool和Task如果要用多线程的话优先使用Task如果版本不支持Task则考虑ThreadPool
4、Parallel
Parallel循环开启多线程并行任务对于多线程开启任务开启的顺序都是不确定的
Parallel.Invoke() Action[] action new Action[] {()Console.WriteLine($线程{Thread.CurrentThread.ManagedThreadId}),()Console.WriteLine($线程{Thread.CurrentThread.ManagedThreadId}),()Console.WriteLine($线程{Thread.CurrentThread.ManagedThreadId}),()Console.WriteLine($线程{Thread.CurrentThread.ManagedThreadId}),()Console.WriteLine($线程{Thread.CurrentThread.ManagedThreadId}),()Console.WriteLine($线程{Thread.CurrentThread.ManagedThreadId}),()Console.WriteLine($线程{Thread.CurrentThread.ManagedThreadId}),()Console.WriteLine($线程{Thread.CurrentThread.ManagedThreadId}),()Console.WriteLine($线程{Thread.CurrentThread.ManagedThreadId}),()Console.WriteLine($线程{Thread.CurrentThread.ManagedThreadId}),
};
Parallel.Invoke(action); 相当于 Action[] action new Action[] {()Console.WriteLine($线程{Thread.CurrentThread.ManagedThreadId}),()Console.WriteLine($线程{Thread.CurrentThread.ManagedThreadId}),()Console.WriteLine($线程{Thread.CurrentThread.ManagedThreadId}),()Console.WriteLine($线程{Thread.CurrentThread.ManagedThreadId}),()Console.WriteLine($线程{Thread.CurrentThread.ManagedThreadId}),()Console.WriteLine($线程{Thread.CurrentThread.ManagedThreadId}),()Console.WriteLine($线程{Thread.CurrentThread.ManagedThreadId}),()Console.WriteLine($线程{Thread.CurrentThread.ManagedThreadId}),()Console.WriteLine($线程{Thread.CurrentThread.ManagedThreadId}),()Console.WriteLine($线程{Thread.CurrentThread.ManagedThreadId}),
};
for (int i 0; i action.Length; i)
{Task.Run(action[i]);
} Invoke时也可以进行一些配置例如配置线程池中只能最多保持一个线程 Action[] action new Action[] {()Console.WriteLine($线程{Thread.CurrentThread.ManagedThreadId}),()Console.WriteLine($线程{Thread.CurrentThread.ManagedThreadId}),()Console.WriteLine($线程{Thread.CurrentThread.ManagedThreadId}),()Console.WriteLine($线程{Thread.CurrentThread.ManagedThreadId}),()Console.WriteLine($线程{Thread.CurrentThread.ManagedThreadId}),()Console.WriteLine($线程{Thread.CurrentThread.ManagedThreadId}),()Console.WriteLine($线程{Thread.CurrentThread.ManagedThreadId}),()Console.WriteLine($线程{Thread.CurrentThread.ManagedThreadId}),()Console.WriteLine($线程{Thread.CurrentThread.ManagedThreadId}),()Console.WriteLine($线程{Thread.CurrentThread.ManagedThreadId}),
};
Parallel.Invoke(new ParallelOptions()
{MaxDegreeOfParallelism 1
}, action); 运行结果 Parallel.For()
//将迭代的结果保存起来
ParallelLoopResult plr Parallel.For(1, 10, (i)
{Console.WriteLine($线程{Thread.CurrentThread.ManagedThreadId});
});
Console.WriteLine(plr.IsCompleted);
相当于 for (int i 1; i 10; i)
{Task.Run(() {Console.WriteLine($线程{Thread.CurrentThread.ManagedThreadId});});
} 相对于循环Task.Run()更加简洁
Parallel.ForEach()
方法和foreach类似不过是采用的是异步方式遍历要想被Parallel.ForEach()必须实现IEnumerable接口
Parallel.ForEachString(new ListString() {a,b,c,d,e,f,g,h,i
}, (str)
{Console.WriteLine(str);
});
运行结果 停止循环的方法 //将迭代的结果保存起来
ParallelLoopResult plr Parallel.For(1, 10, (i,state)
{Console.WriteLine($线程{Thread.CurrentThread.ManagedThreadId});if (i4) {//结束state.Break();}
});
Console.WriteLine(plr.IsCompleted);