了解一些多线程相关的知识


一、基础知识
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

posted @ 2017-09-18 22:02  zhixin9001  阅读(157)  评论(0编辑  收藏  举报