(29)C#多线程
使用线程的原因
1.不希望用户界面停止响应。
2.所有需要等待的操作,如文件、数据库或网络访问需要一定的时间。
一个进程的多个线程可以同时运行不同cpu或多核cpu的不同内核上
注意多线程访问相同的数据必须实现同步机制
编写能够利用并行性的代码需要区分两种场景:任务并行性和数据并行性
任务并行性:使用CPU的代码被并行化,利用cpu的多个核心快速的完成包含多个任务的活动。
数据并行性:使用了数据集合,在集合上执行的工作被划分为多个任务。
任务并行性和数据并行性可以混合起来。
Parallel类(自 4.0 起可用)
Parallel类定义了并行的for和foreach的静态方法,使用多线程来完成作业。
Parallel.For()和Parallel.ForEach()方法再每次迭代中调用相同的代码。而Parallel.Invoke()方法允许同时调用不同的方法。
Parallel.Invoke()用于任务并行性,Parallel.Invoke()用于数据并行性。
Parallel.For()
using System.Threading;
using System.Threading.Tasks;
class Program
{
static void Main(string[] args)
{ //参数1 int ,参数2 int,参数3 action<int>
Parallel.For(, , (i)=> {
Console.WriteLine("id:" + i + " thread:" + Thread.CurrentThread.ManagedThreadId);
});
Console.WriteLine("end");
Console.ReadLine();
}
}
参数3的action中可以添加一个控制状态的类型
Action<Int32,ParallelLoopState>)
使用 Break()和Stop()尽早的结束循环,在结束前启动的线程仍可以继续执行。
Break() 尽早结束当前以外的线程
Stop() 尽早结束
class Program
{
static void Main(string[] args)
{ Parallel.For(, , (i, state) => {
if (i > )
{
state.Stop();
Console.WriteLine("abc");
}
Console.WriteLine("id:" + i + " thread:" + Thread.CurrentThread.ManagedThreadId);
});
Console.WriteLine("end");
Console.ReadLine();
}
}
Parallel.ForEach()
用来遍历一个能被迭代的集合,相当于一个多线程的foreach版
class Program
{
static void Main(string[] args)
{
int[] count = { , , , , , , , , };
Parallel.ForEach(count, (i, state) => { Console.WriteLine("id:" + i + " thread:" + Thread.CurrentThread.ManagedThreadId);
});
Console.WriteLine("end");
Console.ReadLine();
}
}
Parallel类返回类型
Parallel.for 和 Parallel.foreach 返回 ParallelLoopResult 类型,用来判断是否执行完成
class Program
{
static void Main(string[] args)
{
ParallelLoopResult res = Parallel.For(3, 10, (i, state) => {
Console.WriteLine("id:" + i + " thread:" + Thread.CurrentThread.ManagedThreadId);
});
Console.WriteLine(res.IsCompleted);
Console.ReadLine();
}
}
Parallel.Invoke()
public static void Invoke (params Action[] actions) ,可以调用多个不同的方法
class Program
{
static void Main(string[] args)
{
Parallel.Invoke(a,b);
Console.WriteLine("end");
Console.ReadLine();
} static public void a()
{
Console.WriteLine("a");
}
static public void b()
{
Console.WriteLine("b");
}
}
任务
1.工厂类的方式启动一个线程
static void Main(string[] args)
{
var tf = new TaskFactory();
Task task = tf.StartNew(abc);
Console.WriteLine("main:"+Thread.CurrentThread.ManagedThreadId);
Console.ReadLine();
} static public void abc()
{
Console.WriteLine("abc:"+ Thread.CurrentThread.ManagedThreadId);
}
2.Task的静态Factory属性启动线程
static void Main(string[] args)
{ Task task = Task.Factory.StartNew(abc);
Console.WriteLine("main:"+Thread.CurrentThread.ManagedThreadId);
Console.ReadLine();
}
3.使用Task的构造函数
实例化后不会立即启动线程,当对象调用Start()方法时才开始启动
static void Main(string[] args)
{ Task task =new Task(abc);
task.Start();
Console.WriteLine("main:"+Thread.CurrentThread.ManagedThreadId);
Console.ReadLine();
}
4.使用Task的静态Run方法
static void Main(string[] args)
{
Task task =Task.Run(abc);
Console.WriteLine("main:"+Thread.CurrentThread.ManagedThreadId);
Console.ReadLine();
}
5.使用Task同步运行
static void Main(string[] args)
{
Task task =new Task(abc);
task.RunSynchronously();
Console.WriteLine("main:"+Thread.CurrentThread.ManagedThreadId);
Console.ReadLine();
}
6.使用单独的线程,而不是线程池
static void Main(string[] args)
{
Task task =new Task(abc,TaskCreationOptions.LongRunning);
task.Start();
Console.WriteLine("main:"+Thread.CurrentThread.ManagedThreadId);
Console.ReadLine();
}
7.带返回值的任务
Func<> 返回Task<TResult>
static void Main(string[] args)
{
Task<int> task =Task<int>.Run(()=>{
Console.WriteLine("");
return ;
});
Console.WriteLine(task.Result);
Console.WriteLine("abc");
Console.ReadLine();
}
task.Result会等到Task执行完才会被调用相当于调用的wait,所以abc会在结果打印之后再打印
8.连续的任务
ContinueWith
static void Main(string[] args)
{
Task task1 = new Task(a);
Task task2 = task1.ContinueWith(b);
Task task3 = task2.ContinueWith(c);
task1.Start();
Console.WriteLine("end");
Console.ReadLine();
} static public void a()
{
Console.WriteLine("a:" + Thread.CurrentThread.ManagedThreadId);
} static public void b(Task t)
{
Console.WriteLine("b:" + Thread.CurrentThread.ManagedThreadId);
} static public void c(Task t)
{
Console.WriteLine("c:" + Thread.CurrentThread.ManagedThreadId);
}
9.连续任务控制
使用TaskContinuationOptions的枚举成员,上一步出现某种问题时,来确定是否执行现在的方法
static void Main(string[] args)
{
Task task1 = new Task(a);
Task task2 = task1.ContinueWith(b,TaskContinuationOptions.DenyChildAttach);
task1.Start();
Console.WriteLine("end");
Console.ReadLine();
}
10.任务的层次
11.
----------------------------------
Thread类
class Program
{
static void Main(string[] args)
{
Thread t1 = new Thread(M);
t1.Start();
//Thread.Sleep(1000);
Console.WriteLine("BBB");
Console.ReadKey();
} static void M()
{ Console.WriteLine("AAA");
}
}
先输出AAA,还是BBB取决于操作系统调度
委托方式,输出结果和上列子相同
class Program
{
static void Main(string[] args)
{
Thread t1 = new Thread(() => Console.WriteLine("AAA"));
t1.Start();
Thread.Sleep();
Console.WriteLine("BBB");
Console.ReadKey();
}
}
给线程传递数据
方法一,启动线程时传递参数
class Program
{
static void Main(string[] args)
{
C c = new C { Message="hello" };
Thread t = new Thread(ThreadM);
t.Start(c);//线程启动时传参
Console.ReadKey();
} static void ThreadM(object O)
{
C c = (C)O;
Console.WriteLine("AAA:{0}",c.Message);
}
}
public class C
{
public string Message;
}
方法二、自定义类
class Program
{
static void Main(string[] args)
{
C c = new C ("hello");
Thread t = new Thread(c.ThreadMain);
t.Start();
Console.ReadKey();
}
}
public class C
{
public string Message;
//构造函数
public C(string Message)
{
this.Message = Message;
} public void ThreadMain()
{
Console.WriteLine("AAA:{0}",Message);
}
}
后台线程
前台线程可以有多个,后台线程也可以有多个。只要有一个前台线程在运行,程序的Main方法就不算结束。
Thread类创建的是前台线程。线城池中的线程时后台线程。
static void Main(string[] args)
{
//IsBackground默认为false,表示为前台线程
Thread t = new Thread(ThreadMain) { Name="线程",IsBackground=false};
t.Start();
Console.WriteLine("AAA");
Console.ReadKey();
} public static void ThreadMain()
{
Console.WriteLine("{0}启动",Thread.CurrentThread.Name);
Thread.Sleep();
}
}
如果IsBackground=false,按任意键后,会等执行完sleep方法才会结束。
如果IsBackground=true,按任意键后会立即结束。
这说明,如果有一个前台线程没执行完,Main方法就算都执行完,也要等到前台程序执行完才算是结束。
线程优先级
线程的priority属性可以控制线程的优先级
它是一个枚举类型
Highest > AboveNormal > Normal > BelowNormal > Lowest
class Program
{
static void Main(string[] args)
{
Thread t1 = new Thread(ThreadMain) { Name = "A", Priority = ThreadPriority.Lowest };
Thread t2 = new Thread(ThreadMain) { Name = "B", Priority = ThreadPriority.Highest };
Thread t3 = new Thread(ThreadMain) { Name = "C", Priority = ThreadPriority.Normal };
t1.Start();
t2.Start();
t3.Start();
Console.ReadKey();
}
public static void ThreadMain()
{
Thread.Sleep();
for (int i = ; i < ; i++)
{
Console.Write("{0}", Thread.CurrentThread.Name);
}
}
}
线程优先级高的占用的CPU会更多一些
线程问题
争用条件
如果两个或多个线程访问相同的对象,并且对共享状态的访问没有同步,就会出现争用条件。
解决办法:加一个lock锁,只能锁引用类型
class Program
{
static object o = new object();
static void Main(string[] args)
{ }
public static void a()
{
lock (Program.o)
{
.....
}
}
}
死锁
锁定过多可能会引发死锁问题
同步
(29)C#多线程的更多相关文章
- java 多线程 29 :多线程组件之 Exchanger
Exchanger Exchanger,从名字上理解就是交换.Exchanger用于在两个线程之间进行数据交换,注意也只能在两个线程之间进行数据交换.线程会阻塞在Exchanger的exchange方 ...
- Java多线程系列目录(共43篇)
最近,在研究Java多线程的内容目录,将其内容逐步整理并发布. (一) 基础篇 01. Java多线程系列--“基础篇”01之 基本概念 02. Java多线程系列--“基础篇”02之 常用的实现多线 ...
- Java多线程系列
一.参考文献 1.:Java多线程系列目录 (一) 基础篇 01. Java多线程系列--“基础篇”01之 基本概念 02. Java多线程系列--“基础篇”02之 常用的实现多线程的两种方式 03. ...
- iOS开发进阶-实现多线程的3种方法
相关文章链接: 1.多线程简介 2.实现多线程的3种方法 ......待续 前言 在多线程简介中,我已经说明过了,为了提高界面的流畅度以及用户体验.我们务必要把耗时的操作放到别的线程中去执行,千万不要 ...
- iOS多线程的三种方法
前言 在多线程简介中,我已经说明过了,为了提高界面的流畅度以及用户体验.我们务必要把耗时的操作放到别的线程中去执行,千万不要阻塞主线程.iOS中有以下3种多线程编程方法: NSThread Grand ...
- Java多线程系列目录(转)
转载方便自己学习,转自:Java多线程系列目录(共43篇) http://www.cnblogs.com/skywang12345/p/java_threads_category.html 最近,在研 ...
- 多线程-Thread-Runnable
一.多线程 1.基本概念 进程:正在运行中的程序,一个进程中至少包含一个线程 线程:进程的任务,执行任务的一个通道,一个进程中可以包含多个线程 2.多线 ...
- 40道经典java多线程面试题
40道经典java多线程面试题 题目来源 看完了java并发编程的艺术,自认为多线程"大成",然后找了一些面试题,也发现了一些不足. 一下问题来源于网上的博客,答案均为本人个人见解 ...
- 推荐Java基础
(一) 基础篇 01. Java多线程系列--“基础篇”01之 基本概念 02. Java多线程系列--“基础篇”02之 常用的实现多线程的两种方式 03. Java多线程系列--“基础篇”03之 T ...
随机推荐
- 剑指Offer - 九度1385 - 重建二叉树
剑指Offer - 九度1385 - 重建二叉树2013-11-23 23:53 题目描述: 输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树.假设输入的前序遍历和中序遍历的结果中都不含重复的 ...
- 剑指Offer - 九度1362 - 左旋转字符串(Move!Move!!Move!!!)
剑指Offer - 九度1362 - 左旋转字符串(Move!Move!!Move!!!)2013-11-23 03:05 题目描述: 汇编语言中有一种移位指令叫做循环左移(ROL),现在有个简单的任 ...
- 《Cracking the Coding Interview》——第6章:智力题——题目2
2014-03-19 06:57 题目:对于8x8的棋盘,如果拿掉对角位置的两个小块儿,能否用1x2的多米诺牌拼成剩下的棋盘? 解法:不可能.且不说8x8,NxN都是不可能的.如果N是奇数,NxN-2 ...
- FTP被动模式服务器端开有限的端口
很多服务器上都搭建的有FTP服务,FTP服务有两种连接模式:主动模式和被动模式.关于这两种模式的介绍,请参考这篇文章:重温FTP的主动模式和被动模式 关于这两种模式的比较,原文有这样的描述: 主动模式 ...
- DWZ(J-UI)之路:错误
1:关于左侧点击会把右边小窗口替换掉,导致右边永远只有一个小窗口. 方法:因为缺少了这个—— <li><a href="/admin/demo/index" ta ...
- Megacli查看Dell服务器Raid状态
通常我们使用的DELL/HP/IBM三家的机架式PC级服务器阵列卡是从LSI的卡OEM出来的,DELL和IBM两家的阵列卡原生程度较高,没有做太多封装,可以用原厂提供的阵列卡管理工具进行监控:而HP的 ...
- ctags+cscope替换sourceinsight
背景 windows环境开发+linux交叉编译的开发模式,代码阅读和编写都用的source-insight. 除了检索,跳转,工程构建等方面,sourceinsight自带了一些宏语言,可以轻松实现 ...
- php中变量的详细介绍
变量的含义: 用于存储信息的容器,在程序运行期间,可以变化的量 变量的命名规则: 1.变量以$符开始 2.变量名只能以字母或下划线开始 3.变量名只能是字母,下划线,数字,不能有特殊字符:逗号,句号, ...
- HDU 3577 Fast Arrangement ( 线段树 成段更新 区间最值 区间最大覆盖次数 )
线段树成段更新+区间最值. 注意某人的乘车区间是[a, b-1],因为他在b站就下车了. #include <cstdio> #include <cstring> #inclu ...
- hdu1576逆元的一道水题
hdu 1576 Problem Description 要求(A/B)%9973,但由于A很大,我们只给出n(n=A%9973)(我们给定的A必能被B整除,且gcd(B,9973) = 1). ...