一. 概念及应用 :
1.概念
多线程(英语:multithreading),是指从软件或者硬件上实现多个线程并发执行的技术。在一个程序中,这些独立运行的程序片段叫作“线程”(Thread),利用它编程的概念就叫作“多线程处理(Multithreading)”。具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程(台湾译作“执行绪”),进而提升整体处理性能。
2. 应用
2.1.多线程程序一般被用来在后台执行耗时的任务
2.1.在方法中完成一个复杂的工作
3.1.多线程在当一个任务比较耗时(比如一个应用服务器,数据库服务器,或者一个客户端的响应)特别有意义。用工作线程完成任务意味着主线程可以立即做其它的事情。
二.用法:
2.1.Thread类:
作用:创建和控制线程。
它的构造函数有两个重载,接受ThreadStart和ParameterizedThreadStart类型的委托参数,创建Thread对象后可以使用Start方法启用线程。
Thread thread= new Thread(new ThreadStart(work1));
thread.Start();
使用ParameterizedThreadStart委托,可以带有一个object类型的参数
Thread thread = new Thread(newParameterizedThreadStart(work2));
thread.Start("QSDB");
publicvoid work2(object arg)
{
string str=(string)arg;
Console.WriteLine(str);
}
自定义类通过构造函数传递参数
classMyThread
{
private string data;
public MyThread(string Data)
{
this.data = Data;
}
public void Work2()
{
Console.WriteLine(data);
}
}
static void Main(string[] args)
{
MyThread mythread = newMyThread("QSDB");
Thread t = new Thread(mythread.Work2);
t.Start();
}
2.2 .线程池
ThreadPool静态类通过QueueUserWorkItem()方法将工作函数排入线程池,每排入一个函数等于请求创建一个线程。
语法:
ThreadPool.QueueUserWorkItem (WaitCallback)
ThreadPool.QueueUserWorkItem (WaitCallback,object)
Example 1.1
2.3.比较 ThreadPool和Thread
ThreadPool
1.所有线程均为后台线程
2.不能设置优先级,不能命名
3.所有池中的线程都是多线程单元(MTA)线程。(许多COM对象都需要单线程单元(STA))
Thread
1.允许创建前台线程
2.可设置优先级,命名
3.t.SetApartmentState(ApartmentState.STA);
三.多线程之间的通信
3.1.ManualResetEvent类、AutoResetEvent类
允许线程通过发信号互相通信。 通常,当线程需要独占访问资源时使用该类
共同点:
WaitOne 来等待信号,Set 来通知资源可用,Reset方法将事件状态设置为非终止状态,导致线程阻止;
可以通过构造函数的参数值来决定其初始状态,若为true则事件为终止状态从而使线程为非阻塞状态,为false事件为非终止状态,线程为阻塞状态。
不同点:
A、AutoResetEvent.WaitOne()每次只允许一个线程进入,当某个线程得到信号后,AutoResetEvent会自动又将信号置为不发送状态,则其他调用WaitOne的线程只有继续等待,也就是说AutoResetEvent一次只唤醒一个线程;
B、ManualResetEvent则可以唤醒多个线程,因为当某个线程调用了ManualResetEvent.Set()方法后,其他调用WaitOne的线程获得信号得以继续执行,而ManualResetEvent不会自动将信号置为不发送。
C、除非手工调用了ManualResetEvent.Reset()方法,则ManualResetEvent将一直保持有信号状态,ManualResetEvent也就可以同时唤醒多个线程继续执行。
Example 1.2
四.锁
作用:每个线程都有自己的资源,但是代码区是共享的,即每个线程都可以执行相同的函数。这可能带来的问题就是几个线程同时执行一个函数,导致数据的混乱,产生不可预料的结果,因此需要用到锁确保拥有锁的线程只有一个。
4.1. lock和monitor
lock语法: lock(expression) statement_block
Monitor:
Monitor等同于lock,用Monitor.Enter(expression)方法是获取锁,Monitor.Exit(expression)方法是释放锁。
Example 1.3Mutex-->ExecuteDataSetImpl
Monitor另有一个TryEntry方法,可以设置一个锁定超时,单位是毫秒
MSDN上不建议锁定的对象为public,常见的结构 lock (this)、lock(typeof (MyType)) 和 lock ("myLock") 也是不推荐的
Lock字符串的问题
原因是在.NET中,字符串会被暂时存放,如果两个变量的字符串内容相同的话,.NET会把暂存的字符串对象分配给该变量。所以如果有两个地方都在使用lock(“mylock”)的话,它们实际锁住的是同一个对象
LockPublic的问题
其它代码也可能锁定相同对象
Lock this的问题
对象本身可能是公共的,从而被外面的代码锁定
值类型的问题
锁定的是它的副本
最佳做法是定义一个private 对象来锁定, 或 private static 对象变量来保护所有实例所共有的数据。
4.2 Mutex
Mutex 就像一个C# lock一样,不同的是它可以跨进程.调用WaitOne方法来获取锁,ReleaseMutex方法来释放锁
互斥体有两种类型:局部互斥体和已命名的系统互斥体。如果使用接受名称的构造函数创建 Mutex 对象,则该对象与具有该名称的操作系统对象关联。已命名的系统互斥体在整个操作系统中都可见,可用于同步进程活动。您可以创建多个 Mutex 对象来表示同一个已命名的系统互斥体,也可以使用 OpenExisting方法打开现有的已命名系统互斥体
Example 1.3 Mutex
4.3.Semaphore:
使用 Semaphore 类来控制对资源池的访问。线程通过调用进入信号量 WaitOne 方法,它继承自 WaitHandle 类,并通过调用释放信号量 Release 方法。
每次一个线程进入信号量,信号量的计数将减少,并当一个线程释放该信号量时递增。当计数为零时,后续请求阻止,直到其他线程释放信号量。如果所有线程都已都释放信号量,计数是最大值时指定创建信号量。
信号量分为两种类型: 本地信号量和已命名的系统信号量。如果您创建 Semaphore 对象使用的构造函数接受一个名称,它是与该名称的操作系统信号量相关联。名为的系统个信号量在整个操作系统,都可见,并可用于同步进程的活动。您可以创建多个 Semaphore 对象来表示同一个已命名系统信号量,并且您可以使用 OpenExisting 方法以打开一个现有的已命名系统信号量。
仅在您的进程内存在本地信号量。可供您具有对本地的引用的过程中的任何线程 Semaphore 对象。每个 Semaphore 对象是单独的本地信号量。
Example 1.4 Semaphore
4.4. WaitHandle
此类通常用作同步对象的基类。从 WaitHandle 派生的类定义一个信号传输机制以指示获取或释放对共享资源的独占访问,但使用继承的 WaitHandle 方法在等待访问共享资源时阻止。
使用此类的静态方法阻塞一个线程,直到一个或多个同步对象接收到信号。
WaitHandle.WaitAll等待指定数组中的所有元素都收到信号。
WaitHandle.WaitAny等待指定数组中的任一元素收到信号。
如果传递的句柄超过 64 个,将引发 NotSupportedException。
Example 1.6 Semaphore
4.5 .Timer计时器
作用:周期性地唤醒某些事件:
4.6.异步:
BeginInvoke开启一个异步调用
例子:https://msdn.microsoft.com/en-us/library/2e08f6yc(v=vs.110).aspx
4.6.BackGroundWork类
控件在windows窗体和WPF创建的时候都是邦定在线程中的,UI中的控件是不允许非创建该控件的线程去调用,唯一可以从创建它的线程之外调用的方法是Invoke(),beginInvoke(),EndInvoke()和InvokeRequired属性
BackGroundWork类就是异步事件模式的一种实现方法。
BackgroundWorker类位于System.ComponentModel命名空间下,主要用来异步执行一个长时间的操作,然后,在完成事件中安全更新UI的控件属性。典型用法如下:
Example:WindowsFormsAsyncUI
五.4.0新增性能
Parallel类,(System.Threading.Tasks)
Parallel会自动决定启用多少线程来完成指定作业。
Example 1
//在Parallel.For方法中,前两个参数定义了循环的开头和结束。示例从0迭代到9。第3个参数是一个Action<int>委托。
Parallel.For(0,10, i =>
{
Console.WriteLine("i:{0},Thread ID:{1}", i,Thread.CurrentThread.ManagedThreadId);
});
Console.WriteLine("Done");
执行结果:
Example 1.4 Semaphore ,用parallel.For方式
Parallel.For(0, tckList.Count, i => {
Console.WriteLine("Calculating {0}'s VolSpot and VolValue;Thread{1}...", tckList[i].ToString(), Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(1000);
});
Console.WriteLine("Done");
Parallel.For(TLocal)
Parallel.For<TLocal> 方法 (Int32,Int32, ParallelOptions, Func<TLocal>, Func<Int32, ParallelLoopState,TLocal, TLocal>, Action<TLocal>)
分为三个部分,第一部分(Init)参数类型是func<TLocal>,这个例子中是Func<String>,即返回String类型的方法。这个方法用于执行迭代的每个线程调用一次
第二部分(body)例子中参数类型是Func<int,ParallelLoopState,String>第一个参数是循环迭代,第二个参数允许停止循环,第三个参数,接受从Init部分返回的值,该循环体还需要一个返回值。
第三部分(finally)本例中接受一个字符串。该方法仅对于每个线程调用一次,这是一个线程退出方法。
Parallel.For<string>(0, 20, ()=>
{
Console.WriteLine("Init thread {0},task {1}", Thread.CurrentThread.ManagedThreadId,Task.CurrentId);
return "aaa";//这个值会传递给第二部分的str1
},
(i,pls, str1) =>
{
Console.WriteLine("body i {0} str1 {1} thread {2} task {3} loopState{4}", i, str1, Thread.CurrentThread.ManagedThreadId, Task.CurrentId,pls.IsStopped);
return string.Format("i {0}", i);
},
(str)=>//接受第二部分传递的值
{
Console.WriteLine("finally {0}", str);
}
);
执行结果:
CountdownEvent, Barrier
examples
#region 1.1 ThreadPool
protected void LoadRequestsCalcData(RequestPack requestPack)
{
TrackEvent trackEvent = new TrackEvent("LoadRequest" + requestPack.Sequence);
List<RequestInfo> validRequests = new List<RequestInfo>();
int totalCalls = 0;
foreach (RequestInfo ri in requestPack.RequestInfoSet)
{
try
{
DBConnectionHolder dbCnn = myProcessorManager.AcquireResource();
ThreadPool.QueueUserWorkItem(new WaitCallback(LoadRequestCalcDataProc),
new LoadRequestCalcDataProcState(ri, trackEvent, dbCnn, validRequests));//Threadpool example
totalCalls++;
}
catch (Exception e)
{
RequestHelper.SafeSetError(ri, "Loading calc data when calling ThreadPool", e);
}
}
}
/// <summary>
/// an information container is used in ThreadPool callback - LoadRequestCalcDataProc
/// </summary>
class LoadRequestCalcDataProcState
{
public LoadRequestCalcDataProcState(RequestInfo requestInfo, TrackEvent trackEvent, DBConnectionHolder dbCnn, List<RequestInfo> validList)
{
myRequestInfo = requestInfo;
myTrackEvent = trackEvent;
myDBCnn = dbCnn;
myValidList = validList;
}
public RequestInfo RequestInfo
{
get { return myRequestInfo; }
}
public DBConnectionHolder DBCnn
{
get { return myDBCnn; }
}
public void OnCompleted()
{
if (myRequestInfo.WorkingStatus == RequestInfo.Status.CalcDataReady)
{
lock (myValidList)
{
myValidList.Add(myRequestInfo);
}
}
myTrackEvent.OnCompleted();
}
private RequestInfo myRequestInfo;
private TrackEvent myTrackEvent;
private List<RequestInfo> myValidList;
private DBConnectionHolder myDBCnn;
}
/// Main job
/// </summary>
protected void LoadRequestCalcDataProc(object state)
{
LoadRequestCalcDataProcState stateInfo = state as LoadRequestCalcDataProcState;
RequestInfo ri = stateInfo.RequestInfo;
//do job
}
#endregion
#region 1.2 ManualResetEvent
private ManualResetEvent myStopEvent = new ManualResetEvent(false);//Stop the service
private ManualResetEvent myUninitializedEvent = new ManualResetEvent(true);//Uninitialize
private List<WaitHandle> myStopHandles = new List<WaitHandle>();
static void Main(string[] args)
{
App.Init(args, typeof(QSDBSchedulerApp).Assembly);
ourApp = (QSDBSchedulerApp)App.Host;
ourApp.Startup += new CancelEventHandler(ourApp.StartupHandler);
ourApp.Shutdown += new EventHandler(ourApp.ShutdownHandler);
ourApp.Stopping += new CancelEventHandler(ourApp.StoppingHandler); // stop event handler
App.Run();
}
/// <summary>
/// main process
/// </summary>
/// <param name="e"></param>
protected override void OnRun(EventArgs e)
{
try
{
ourLogger.Info("App::OnRun");
myExitCode = (int)ExitCode.DefaultError;
if (DoInitialization())
{
myUninitializedEvent.Reset();
ourLogger.Info("=============== Startup Completed ===================");
ourLogger.Info("Wait for stop signal");
myStopEvent.WaitOne(); // wait stop signal
ourLogger.Info("Received stop signal");
myExitCode = (int)ExitCode.Success;
}
}
catch (Exception exc)
{
ourLogger.Fatal("OnRun", exc);
TerminateApplication(); // a hack to stop the app. Otherwise, ...
}
// Stop the application
DoUnInitialization(); // UnInitial
myUninitializedEvent.Set();
ourLogger.Info("App::OnRun exit");
}
/// <summary>
/// Handle stop event to stop the application
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void StoppingHandler(object sender, EventArgs e)
{
ourLogger.Info("StoppingHandler: enter");
myStopEvent.Set();
int timeout = (myOption == null ? 0 : myOption.ShutdownGracefullyPeriod) + 100;
if (!myUninitializedEvent.WaitOne(timeout, true))
{
ourLogger.Warn("Uninitialization timeouted");
}
Thread.Sleep(50); // let it exit OnRun
}
protected virtual void DoUnInitialization()
{
try
{
//dequeue request log execption
ourLogger.Info("Doing uninitialization");
if (!WaitHandle.WaitAll(myStopHandles.ToArray(), myOption.ShutdownGracefullyPeriod, true))
{
ourLogger.WarnFormat("Wait stop signal timeouted: {0}", myOption.ShutdownGracefullyPeriod);
}
ourLogger.Info("Uninitialization done!");
}
catch (Exception e)
{
ourLogger.Error("DoUnInitialization in shutdown", e);
}
}
#endregion
#region 1.3 Mutex
private static string LockMutex = @"Global\SchedulerMutex";
private static object ourSync = new object();
public static void ExecuteNonQueryMutex(IDbConnection conn, string sql)
{
Mutex mk = GetMutex();
try
{
ourLogger.Info("Acquiring Mutex...");
mk.WaitOne();
ourLogger.Info("Acquired Mutex...");
ExecuteDataSetImpl(conn, sql);
}
finally
{
mk.ReleaseMutex();
ourLogger.Info("Released Mutex...");
}
}
private static DataSet ExecuteDataSetImpl(IDbConnection conn, string sql)
{
int retryCount = 0;
Exception error = null;
string connStr = conn.ConnectionString;
while (retryCount < retryTime)
{
try
{
if (conn.State == ConnectionState.Closed)
{
ourLogger.Info("Recreate connection.");
conn = CreateConnection(connStr, conn);
}
ourLogger.Info("before execute:" + sql + ";ConnectionState:" + conn.State);
lock (ourSync)// Monitor.Enter(ourSync);
{
Random r = new Random(DateTime.Now.Millisecond);
int sleep = r.Next(0, 1000);
Thread.Sleep(sleep);
return DBUtil.ExecuteDBQuery(sql, conn);
}//Monitor.Exit(ourSync);
}
catch (Exception ex)
{
error = ex;
retryCount++;
if (conn != null && conn.State != ConnectionState.Closed)
{
conn.Close();
}
conn = CreateConnection(connStr, conn);
ourLogger.Error("Retry: " + sql + " for the " + retryCount + " times");
Thread.Sleep(retryInterval);
}
finally
{
ourLogger.Info("after execute:" + sql);
}
}
throw error;
}
public static Mutex GetMutex()
{
Mutex result = null;
try
{
result = Mutex.OpenExisting(LockMutex);
}
catch (WaitHandleCannotBeOpenedException)
{
result = new Mutex(false, LockMutex);
}
return result;
}
#endregion
#region 1.4 Semaphore
class Class1
{
private static object ourSync = new object();
internal class VolCacheCalculator
{
private static object syncObj = new object();
internal List<VolResult> Calculate()
{
List<UndEntity> tckList = null;
SuperWaitHandle swh = new SuperWaitHandle();
System.Threading.Semaphore resource = new System.Threading.Semaphore(20, 20);//设置初始信号量为20
foreach (UndEntity tck in tckList)
{
StateWrap wrap = new StateWrap();
wrap.TCK = tck;
wrap.WaitEvent = swh.CreateWaitEvent();//增加一个AutoResetEvent到list中
ThreadPool.QueueUserWorkItem(delegate(object state)
{
IDbConnection conn = null;
StateWrap dataPack = (StateWrap)state;
string symbol = dataPack.TCK.Symbol;
int eod = dataPack.TCK.Eod;
double spot = dataPack.TCK.Spot;
try
{
resource.WaitOne();//占有一个信号量。超过20个后,阻塞线程
conn = MyDBUtil.CreateConnection(connOutter.ConnectionString);
string sql = string.Format(sqlTemplate, symbol, eod);
// force to execute GC
GC.Collect();
GC.WaitForPendingFinalizers();
DataSet ds = DBUtil.ExecuteDBQuery(sql, conn);
DataTable dtSource = ds.Tables[0];
List<VolResult> volCacheResult = BuildVolSurface(symbol, eod, spot, dtSource);
if (volCacheResult != null)
{
lock (syncObj)
{
result.AddRange(volCacheResult);
}
}
}
catch (Exception ex)
{
ourLogger.Error("Failed when build vol for " + symbol, ex);
}
finally
{
lock (syncObj)
{
using (StreamWriter sw = new StreamWriter(@"Complete.txt", true))
{
sw.WriteLine(symbol);
}
}
resource.Release();//释放一个信号量
dataPack.WaitEvent.Set();
}
}, wrap);
}
swh.WaitAll();//等待数组中的所有元素都收到信号。
return result;
}
private List<UndEntity> GetTickerList(IDbConnection conn)
{
List<UndEntity> list = new List<UndEntity>();
return list;
}
internal class UndEntity
{
public string Symbol { get; set; }
public int Eod { get; set; }
public double Spot { get; set; }
}
internal class VolResult
{
public string Symbol { get; set; }
public int Eod { get; set; }
public double Moneyness { get; set; }
public string Term { get; set; }
public double AvgImp { get; set; }
public double BA_spread { get; set; }
public double pc_spread { get; set; }
}
internal class StateWrap
{
public UndEntity TCK;
public AutoResetEvent WaitEvent;
}
internal class SuperWaitHandle
{
List<AutoResetEvent> list = new List<AutoResetEvent>();
public AutoResetEvent CreateWaitEvent()
{
AutoResetEvent autoEvent = null;
lock (this)
{
do
{
autoEvent = new AutoResetEvent(false);
}
while (autoEvent == null || list.Contains(autoEvent));
list.Add(autoEvent);
}
return autoEvent;
}
public void Reset()
{
list = new List<AutoResetEvent>();
}
public bool WaitAll()
{
List<AutoResetEvent> temp = new List<AutoResetEvent>(64);
for (int i = 0; i < list.Count; i++)
{
if (temp.Count == 64)
temp = new List<AutoResetEvent>(64);
if (temp.Count < 64)
{
temp.Add(list[i]);
if (temp.Count == 64 || temp.Contains(list.LastOrDefault()))
{
WaitHandle.WaitAll(temp.ToArray());//AutoResetEvent的线程组,waitall阻止线程,直到所有线程都收到信号
}
}
}
return true;
}
}
}
}
#endregion