C#多线程总结

本文详细介绍了多线程的概念,包括线程的优缺点、线程的属性和方法,以及如何通过委托、回调和Task进行线程操作。同时,探讨了线程池、Parallel类和Task类在并发编程中的应用。还提到了线程的控制与取消,以及锁的概念。最后,文章阐述了async/await关键字在异步编程中的作用和异步方法的实现方式。
摘要由CSDN通过智能技术生成

多线程理论:
1.高耗资源,高速率性,高性能。甚至短时间内,把CPU撑满。
2.无序性,不能确定哪个线程先运行完(同级)。

委托开启多线程:
注意:这个方式开启的线程来源于线程池。beginInvoke是异步的,Invoke是同步的;也就是说,beginInvoke相当于开启线程来执行,而Invoke执行方法是单线程执行,会卡住。
beginInvoke有两个参数,第一个参数是一个AsyncCallback的委托,用于判断线程是否运行结束;第二个参数是obj类型的,这个参数会变成第一个参数AsyncState的值。BeginInvoke是有返回值的,返回值也可以用来判断线程的执行情况。

利用回调方法操作:
Action ac = new Action(Display);
Action ac1 = new Action(Display);
AsyncCallback callback = obj =>
{
Console.WriteLine(obj.AsyncState);
};
ac1.Invoke();
ac.BeginInvoke(callback, “运行完了”);

解析:callback是BeginInvoke的回调函数,BeginInvoke执行完会自动调用它,它是一个委托,参数类型是一个IAsyncResult,而BeginInvoke的返回值也是IAsyncResult。BeginInvoke的第二个参数是IAsyncResult的AsyncState值。因此可以在callback 里面写线程完成后的操作。

利用委托返回指操作:
Action ac = new Action(Display);
IAsyncResult result =ac.BeginInvoke(null, null);
// 等上面线程运行结束,类似于Join
result.AsyncWaitHandle.WaitOne(); //可以设置时间
if (result.IsCompleted)
{
Console.WriteLine(“运行结束”);
}

[Obsolete]
使用EndInvoke获取执行方法返回值,但是用这种方法就变成单线程了,所以不考虑用:
IAsyncResult result =fc.BeginInvoke(null, null);
string str = fc.EndInvoke(result);

Thread类:
属性:
IsBackground:是否设置为后台线程
CurrentThread:获取当前运行的线程
IsAlive:如果此线程已启动并且尚未正常终止或中止,则为 true;否则为 false。
Priority:线程优先级,有5个等级,最高值是4,最低值是0.
ManagedThreadId:获取线程ID.
ThreadState:线程的状态,注意设置了IsBackgroud的线程状态是IsBackgroud,而非Running
Name:线程名,设置了才有,默认为null

静态方法:
Sleep:休眠,单位为毫秒,如果为-1就是无限休眠

方法:
1.销毁线程:thread1.Abort();
2.开始线程:testThread.Start();
3.阻塞线程:testThread.Join(); //卡在这里,直到线程testThread运行完,不然不往下进行。

带参数的方法:
Thread t = new Thread(show);
t.Start(0);
注意:Show中的参数应该是Obj,而不是其他参数
如:static void show(object num)

缺点:
Thread类有两大缺点,控制并不灵敏,且不容易控制。这也是为什么suspend方法等会被弃用的原因,因为操作系统可能没时间去处理,也因为不容易控制才导致出现了后来的Task等。

[Obsolete]
ThreadPool类:
线程池委托的方法必须带参数object。
WaitCallback callBack = new WaitCallback(Display);
ThreadPool.QueueUserWorkItem(callBack);

方法:
SetMaxThreads:设置线程池最大线程数
SetMinThreads:设置线程池最小线程数
GetMaxThreads:获取线程池最大线程数
GetMinThreads:获取线程池最小线程数
QueueUserWorkItem:执行线程

Parallel类:
这是一个静态类
主要用于并发的执行某些任务,但是主线程也会被拉进来一起执行,也就是说会卡住
Parallel.Invoke(Display,Display,Display,Display);
Console.WriteLine(“运行完了”);

优势2:可以控制最大并发线程数
ParallelOptions options = new ParallelOptions();
options.MaxDegreeOfParallelism = 2;
Parallel.Invoke(options,Display,Display,Display,Display);
Console.WriteLine(“运行完了”);

Task类:
属性:
IsCompleted:是否完成
IsCanceled:是否取消
IsFaulted:是否失败
Status:线程状态:RanToCompletion (成功完成执行任务) Canceled (取消) Faulted (失败)
Id:获取系统分配的ID

静态方法:
Run(Action):在线程池中执行线程。
Factory.StartNew(Action):开启线程
WaitAll(Task[],time):同步等待所有任务运行结束,可以限定时间
WaitAny(Task[],time):同步等待任意一个任务运行结束,可以限定时间,并返回运行完的任务的索引。
WhenAll(Task[]):异步等待所有的任务运行结束。
WhenAny(Task[]):异步等待任一任务运行结束。

方法:
ContinueWith(Action):执行完线程后,执行委托。
GetAwaiter().OnCompleted(Action): 直接运行的线程,运行完后执行委托
Task.Wait task1.Wait();就是等待任务执行(task1)完成,task1的状态变为Completed。
Task.WaitAll 待所有的任务都执行完成:
Task.WaitAny 发同Task.WaitAll,就是等待任何一个任务完成就继续向下执行
Dispose:注意要该任务执行完才能调用,否则要报异常
CancellationTokenSoure:通过cancellation的tokens来取消一个Task。这个不属于Task,是用来辅助Task取消的类

利用Task工厂创建任务:
Task task = Task.Factory.StartNew(); //创建完直接启动
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
Task t = new Task(() =>
{
Console.WriteLine(“任务开始工作……”);
//模拟工作过程
Thread.Sleep(5000);
});
t.Start();
t.ContinueWith((task) =>
{
Console.WriteLine(“任务完成,完成时候的状态为:”);
Console.WriteLine(“IsCanceled={0}\t IsCompleted={1}\tIsFaulted={2}”, task.IsCanceled, task.IsCompleted, task.IsFaulted);
});
Console.ReadKey();
}
}
}

Thread.Sleep() 和 Task.Delay() 的区别:
Sleep是同步线程休眠,在异步线程中调用会非常糟糕,Delay是异步线程休眠,在同步线程中使用将无任何效果。使用方式如下:
public static async Task Test(int deley)
{
Console.WriteLine(“开始测试”);
await Task.Delay(deley);
Console.WriteLine(deley);
}

暂停任务:
ManualResetEvent resetEvent = new ManualResetEvent(true);
Task task = new Task(() =>
{
for (int i = 0; i < 100; i++)
{
resetEvent.WaitOne();
Console.WriteLine(“我是第一个任务”);
Thread.Sleep(500);
}

});
task.Start();
Console.WriteLine(task.Status.ToString());
Console.WriteLine(“暂停任务”);
resetEvent.Reset();
Console.WriteLine(task.Status.ToString());
Thread.Sleep(5000);
Console.WriteLine(“继续任务”);
resetEvent.Set();
Console.ReadKey();

带参数和返回值的任务:
static void Main(string[] args)
{
//用Task创建带参数和返回值的线程
var resault = Task.Run(() => JudgeStr(100));

   //获取多线程的返回值,注意使用了这个,当前线程会卡在这里,相当于使用了等待
    int m = resault.Result;
    Console.WriteLine(m);
    Console.ReadKey();

}

static int JudgeStr(int num)
{
while (true)
{
if (num < 500)
{
num++;
Thread.Sleep(1);
}

       else
       {
           return num;
       }
 }

}

TaskFactory类:
方法:
1.ContinueWhenAll:等待所有任务组执行完后,执行委托。
TaskFactory t = new TaskFactory();
t.ContinueWhenAll(new Task[] { Task.Run(Display),Task.Run(Display)}, tArry =>
{
Console.WriteLine(“所有任务都完成了”);
});
2.ContinueWhenAny:等待任务组的某一个任务执行完后,执行委托。

取消线程:
CancellationTokenSource类:
主动取消线程。
属性:
Token:获取与此 System.Threading.CancellationToken 关联的 System.Threading.CancellationTokenSource
方法:
Cancel():传达取消请求
Dispose():释放取消类上的资源
CancellationToken类:
属性:
IsCancellationRequested:获取是否已请求取消此标记,取消了是true
ThrowIfCancellationRequested:如果线程被取消了,会抛出异常,所以要捕获异常

代码:
CancellationTokenSource cts = new CancellationTokenSource();
CancellationToken token = cts.Token;
ThreadPool.QueueUserWorkItem(_ =>
{
try
{
for (int i = 0; i < 100; i++)
{
token.ThrowIfCancellationRequested();
Console.WriteLine(i);
Thread.Sleep(100);
}
}
catch
{
}
});
Console.WriteLine(“运行完了”);
Thread.Sleep(500);
cts.Cancel();
cts.Dispose();

锁:
作用:保证只有一个线程操作某个资源。根本原理是单线程化,把多线程转换成单线程。
标准写法:
private static readonly object LOCK = new object();
static void Main(string[] args)
{
List intList = new List();
for (int i = 0; i < 10000; i++)
{
Task.Run(() =>
{
lock (LOCK)
{
intList.Add(i);
}
}
);
}

上面写法等价于:
Task.Run(() =>
{
Monitor.Enter(LOCK);
intList.Add(i);
Monitor.Exit(LOCK);
});

await/async使用:
特点:
1.被async修饰的方法称为异步方法。
2.异步方法的返回类型只能是void,task,task。
3.不含有await的异步方法没有卵用,只能同步执行。
4.await只能等待task或task。
5.一个异步方法中可以有多个await修饰。
6.async和await是异步编程的模型(实际上就相当于一种规范),本身不开启线程。

综合上面,知道async是一种异步方法规范,那么线程便需要手动去开。await其实起到的就是线程阻塞的作用。
例:
三种异步方法:
public async void Display()
{
await Task.Run(() =>
{
Thread.Sleep(3000);
Console.WriteLine(“小黑是一条狗儿”);
});
Console.WriteLine(“void类型的异步方法执行完毕”);
}

public async Task Play()
{
await Task.Run(() =>
{
Thread.Sleep(3000);
Console.WriteLine(“小黑是一条狗儿”);
});
Console.WriteLine(“task的异步方法执行完毕”);
return 123;
}

public async Task Show()
{
await Task.Run(() =>
{
Thread.Sleep(3000);
Console.WriteLine(“小黑是一条狗儿”);
});
Console.WriteLine(“task异步方法执行完毕”);
}

调用:
public async void Operation()
{
MyTask myTask = new MyTask();
await myTask.Show();
Console.WriteLine(“异步方法是不会占用主线程的”);
}

用winform实战:
private async void button1_Click(object sender, EventArgs e)
{
string res = await theHandle();
txt1.Text = res;
}

private async Task theHandle()
{
await Task.Delay(5000);
return “锄禾日当午,汗滴禾下土。谁知盘中餐,粒粒皆辛苦。”;
}

如果改成线程操作:
private void button1_Click(object sender, EventArgs e)
{
string str = null;
Task t = new Task(() =>
{
Thread.Sleep(3000);
str= “锄禾日当午,汗滴禾下土。谁知盘中餐,粒粒皆辛苦。”;
});

    t.Start();
    Task.WaitAll(t);
    txt1.Text = str;

}
用线程方法,在等待时依然会把主线程拉进来,因此会卡界面,但是异步方法就不会,await的等待是异步等待,因此以后写异步方法都采用async。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值