文章目录
前言
- 我们利用线程是为了更好的利用多核pu的资源
- 有这么几个要点,
- 在线程执行完后,我们要如何获得结果
- 线程之间的资源争夺导致的问题
- 线程的执行顺序,其实线程可能由于cpu资源的动态变化,运行顺序完全随机的,不可控
- 线程开启以及消耗导致的资源浪费
- 每种创建线程的方式,给线程传递参数的方法,和获得结果的方法都不一样
利用委托开启线程
步骤
- 将一个方法赋给委托变量
- 变量调用BeginInvoke()方法 即可开启一个线程执行该方法
Func<int, int,int > a = (d, b) => d+b;
IAsyncResult ar = a.BeginInvoke(3, 4,null, null);
传递参数
- BeginInvoke的前几个参数全部是传递参数,其次是回掉函数,最后是 自定义给回调函数传递的参数对象 属于为object类型
获得函数结果
- 利用while 死循环判断线程执行状态,执行结束,就取得结果
//循环判断
while (true)
{
if (ar.IsCompleted == true)
{
int r = a.EndInvoke(ar);
Console.WriteLine("最后的结果是:"+r);
break;
}
}
- 利用指定线程的等待句柄,暂停当前线程一定时间后,如果获得指定线程的响应,就返回true,否则false
Func<int, int,int > a = (d, b) => d+b;
IAsyncResult ar = a.BeginInvoke(3, 4,null, null);
WaitHandle handle = ar.AsyncWaitHandle;
bool res = handle.WaitOne(10); //等待10s获得,如果获得响应,返回true否则false 注意他会阻塞当前线程
if (res==true)
{
int r = a.EndInvoke(ar);
Console.WriteLine(r);
}
- 利用回调函数(最优)
static void Main(string[] args)
{
Func<int, int,int > a = (d, b) => d+b;
a.BeginInvoke(3, 4, CallBack, a);
Console.ReadKey();
}
static void CallBack(IAsyncResult ar) //系统会自动将该参数填充
{
Func<int, int, int> b = ar.AsyncState as Func<int, int, int>;
int res = b.EndInvoke(ar);
Console.WriteLine("最后得到的结果是:"+ res);
}
利用Thread类直接开启线程
- 位于System.Threading下面
- 可以直接new出来一个线程实例,将方法作为构造器参数
- 然后 实例.Start() 才开启线程
Thread t = new Thread(methodName);
t.Start();
传递参数
- 第一种方法
- 可以在函数里面设定一个object类型的参数
- 然后在Start方法里面传递
void DoSm(Object str){ Console.WriteLine("输入的数据:"+str); }
Thread t = new Thread(DoSm);
t.Start("我很好")
- 第二种方法
- 可以定义一个类,类里面包含相应字段成员,然后实例化一个对象,再将对象的方法传递给线程
class Say
{
private string name;
private int age;
public void Say(string name, int age)
{
this.name = name;
this.age = age;
}
public void SayIt()
{
Console.WriteLine("我的名字是:"+name+" "+"年龄为:"+age);
}
}
Class Program
{
static void main(string[] args)
{
Say say = new Say("chaodan", 20);
//创建线程
Thread t = new Thread(say.SayIt);
t.Start();
}
}
- 或者通过Lambda表达式,其参数可以访问上下文的变量
string name = "job";
Thread t = new Thread( ()=>Console.WriteLine(name) );
t.Start()
线程的优先级
- 在Thead类中,可以设置Priority属性,以影响线程的基本优先级 ,Priority属性是一个ThreadPriority枚举定义的一个值。定义的级别有Highest ,AboveNormal,BelowNormal 和 Lowest
线程的状态
- 获取线程的状态,当我们通过调用Thread对象的Start方法,可以创建线程,但是调用了Start方法之后,新线程不是马上进入Running状态,而是出于Unstarted状态,只有当操作系统的线程调度器选择了要运行的线程,这个线程的状态才会修改为Running状态。我们使用Thread.Sleep()方法可以让当前线程休眠进入WaitSleepJoin状态。
- 使用Thread对象的Abort()方法可以停止线程。调用这个方法,会在终止要终止的线程中抛出一个ThreadAbortException类型的异常,我们可以try catch这个异常,然后在线程结束前做一些清理的工作。
- 如果需要等待线程的结束,可以调用Thread对象的Join方法,表示把Thread加入进来,停止当前线程,并把它设置为WaitSleepJoin状态,直到加入的线程完成为止。
注意事项
- Thread创建的线程默认都是前台线程
- 可以通过设置IsBackGround来设置为后台线程
- 前台线程执行完后,会直接关闭进程,不管后台线程有没有执行完毕
- 只要还有一个前台线程没有执行完,就不会关闭进程
Thread t = new Thread(methodName);
t.IsBackGround = true; //设为后台程序
t.Start();
利用线程池开启线程
- 位于System.Threading 下面
- 线程池中的线程都是后台线程
- 不能把入池的线程改为前台线程
- 不能给入池的线程设置优先级或名称
- 入池的线程只能用于时间比较短的任务
- 利用线程池发起一个线程,方法必须要有一个参数
- 这和其定义的委托类型有关系
void ThreadMethod(object name){Console.WriteLine("我的名字:"+name);}
ThreadPool.QueueUserWorkItem(ThreadMethod);
利用任务开启线程
- 可以new 出来一个任务实例,将方法作为构造参数传递,然后开启任务即可
- 通过TaskFctory.StartNew( TaskMethod ); 静态方法来开启
//第一种方法
Task task = new Task(()=>{ Console.WriteLine("大声喊出我的名字!"); });
task.Start();
//第二种方法
TaskFactory sd = new TaskFactory();
sd.StartNew(() => Console.WriteLine(6 + 3));
连续任务
- 如果任务之间有依赖关系 可以使用连续任务
- ContinueWith的函数里面必须要有一个Task的参数
Task task = new Task(() => Console.WriteLine("你好"));
task.Start();
Task task2 = task.ContinueWith((a) => Console.WriteLine("hello"));
任务层次结构
- 在一个任务中启动一个新的任务,相当于新的任务是当前任务的子任务,两个任务异步执行
- 只有子任务全部执行完了,父任务的状态才会变成RunToComPletion
线程资源争夺
- 解决办法 使用lock关键字
- 只有获得锁的线程才能操作该资源
- lock只能锁对象,引用类型
- 死锁问题
- 这篇博客总结的很好
今天的文章C#中创建线程的四种方式分享到此就结束了,感谢您的阅读,如果确实帮到您,您可以动动手指转发给其他人。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/29621.html