一、基础知识
a)多线程的基础类为Thread,实际应用中一般用线程池,而不会直接用到Thread类。Thread基本的使用为:
int i = 5;
Thread thread = new Thread((p) => {
Console.WriteLine(p);
});
thread.Start(i);
可以用lambda写法,也可以指向一个方法,参数通过Start(i)来传递。
b) 线程间的同步有几种方法
可以在一个方法加上[MethodImpl(MethodImplOptions.Synchronized)],这样这个方法就无法被多个线程同时访问了,这属于方法粒度的控制。
lock和Monitor可以提供代码块粒度的控制,用lock(<locker>){}或Monitor.Enter(<locker>)Monitor.Exit(<locker>)包围的代码块可以做到只允许一个线程同时访问。
c) 对于单例模式,如果要保证线程安全,最好用这样的写法:
class Singleton {
private Singleton() { }
private static object locker = new object();
private static Singleton instance = null;
public static Singleton GetSingleton() {
if (instance == null) {
lock (locker) {
instance = new Singleton();
}
}
return instance;
}
}
需要注意的是,只有在instance==null的时候,才进行lock并初始化。
d)线程的控制和线程池
除了锁之外,还可以通过ManualResetEvent、AutoResetEvent来进行线程间的的通讯,以进行更精细的控制。基本的方法为WaitOne、Set、Reset
AutoResetEvent are = new AutoResetEvent(false);
Thread t1 = new Thread(() => {
while (true) {
Console.WriteLine("waitting open");
are.WaitOne();
Console.WriteLine("Open");
}
});
在这里使用WaitOne就相当于为线程的流动加上了闸门,然后配合Set、Reset就可以控制线程的通断了。
一般的场景推荐应用线程池。线程池中的线程使用完后不会被销毁,而是被回收,供下一次使用,这样可以节省反复创建、销毁线程造成的开销。线程池不追求精细化的控制,但能够提供简单的使用多线程的方式,自动进行性能调优,很适合于UI程序中避免程序假死。
二、三种异步编程模型
三种异步编程模型分别为EAP、APM、TPL,TPL方式使用最简洁。
a) EAP(Event-based Asynchronous Pattern)方式使用简单,但不容易实现复杂逻辑,已不推荐使用。EAP使用回调的写法:
private static void EAP() {
WebClient wc = new WebClient();
wc.DownloadStringCompleted += Wc_DownloadStringCompleted;
wc.DownloadStringAsync(new Uri("http://www.github.com"));
Console.WriteLine("Downloading");
}
private static void Wc_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e) {
Console.WriteLine(e.Result);
}
b) APM(Asynchronous Programming Model)方式也不推荐使用了,FileStream还保留有这种写法:
private static void APM() {
string filePath = "e:EFCore.txt";
using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read)) {
var buffer = new byte[128];
var asyncState = new AsyncState { FS = fileStream, Buffer = buffer, EvtHandle = new ManualResetEvent(false) };
IAsyncResult asyncResult = fileStream.BeginRead(buffer, 0, buffer.Length, new AsyncCallback(AsyncReadCallBack), asyncState);
asyncState.EvtHandle.WaitOne();
Console.WriteLine();
Console.WriteLine("read complete");
Console.ReadKey();
}
}
public static void AsyncReadCallBack(IAsyncResult asyncResult) {
var asyncState = (AsyncState)asyncResult.AsyncState;
int readCn = asyncState.FS.EndRead(asyncResult);
if (readCn > 0) {
byte[] buffer;
if (readCn == 128) {
buffer = asyncState.Buffer;
}
else {
buffer = new byte[readCn];
Array.Copy(asyncState.Buffer, 0, buffer, 0, readCn);
}
string readContent = Encoding.UTF8.GetString(buffer);
Console.Write(readContent);
if (readCn < 128) {
asyncState.EvtHandle.Set();
}
else {
Array.Clear(asyncState.Buffer, 0, 128);
asyncState.FS.BeginRead(asyncState.Buffer, 0, 128, new AsyncCallback(AsyncReadCallBack), asyncState);
}
}
}
c) TPL(Task Parallel Library)是在.Net4.0之后的新特性,异步实现起来更简洁、直观。比如FileStream的使用:
async static void TPL() {
string filePath = "e:/文章汇总/083.20170906 .Net Core(二)EFCore.txt";
using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read)) {
var buffer = new byte[128];
var readLength = 0;
while ((readLength = await fileStream.ReadAsync(buffer, 0, buffer.Length)) != 0) {
string content = Encoding.UTF8.GetString(buffer);
Console.WriteLine(content);
}
Console.WriteLine("complete");
}
}
方法要被被async修饰,同时fileStream前使用await,然后就可以用很自然的的编码方式了。WinForm中的事件处理方法、MVC中的Action都可以使用这种方法。
学习资料:如鹏网.net提高班http://www.rupeng.com/News/10/4603.shtml