.net多线程
一、多线程编程方式
常见传统的多线程编程方式new Thread、 ThreadPool.QueueUserWorkItem、 APM模式Begin**,End**,.net提供了很多IO和网络的这类方法。略过..
刚用System.Threading.Timer(call back, state, duetime,period)写了个游戏的小功能。虚拟庄家X先生,每分钟Points必须有增长,否则自动+5
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public static class Xman { public static int Points { get { return _points; } } /// <summary> /// 初始Xman points /// </summary> private static int _points = ParticipateRepository.Instance.GetXmanPoints(); private static int preMin_points = Points; public static int Win(int points) { return Interlocked.Add(ref _points, points); } /// <summary> /// 如果Xman的point没增长,每分钟加5point /// </summary> private static System.Threading.Timer timer = new Timer((state) => { if (preMin_points < Points) { preMin_points = Points; } else { ParticipateRepository.Instance.IncreXmanPoints(Win(5)); } },null,60000, 60000); }
.net 4.0开始推荐使用Task,TaskScheduler调度,对于非LongRunning的task都是放到ThreadPool队列的
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public virtual async Task<bool> LogParticipate(string prizeName) { Task<bool> t = Task.Run(() => { Dictionary<string, string> dicPara = new Dictionary<string, string>(); dicPara.Add("prizeName", prizeName); return MailHelper.Send(_cicid, "Ten Years-Lucky Roulette", dicPara, Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "EmailTemplate"), "Roulette.html"); }); return await t; }
可在t.Wait(),t.Result处捕捉AggregateException,Task间顺序执行ContinueWith, 取消一个task的执行是用CancellationToken
对于创建多个相同的task,建议使用Parallel.Foreach...
二、线程同步
并行要考虑临界资源、执行顺序,最常见的同步方式lock(Monitor.Enter&&Monitor.Exit), Let's see在哪些场景下优先使用的其他方式
1) Interlocked 提供对整数的+,-,compare,exchange原子操作,像Xman的分数处理
2) Mutex,可实现跨域,例如,单应用实例
3) ReaderWriterLock, 用于共享reader的场景
4) MethodImpl和SynchronizationAttribute,使用繁琐,不如直接lock
5) AutoResetEvent、ManualResetEvent,线程之间传递事件,简单通过Set发信号,WaitOne等待信号来控制代码是否往下执行。Auto区别于Manual的是set一次只会有一个线程WaitOne收到信号,此时IsRelease为false。
6) 线程安全的集合Concurrent*,用来作为临界集合资源的数据结构,very nice。例如,轮盘游戏在用户中奖后,优惠券编码等不能相同
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/// <summary> /// key:prize name; value:usable prize /// </summary> private static Dictionary<string, ConcurrentQueue<PrizeDefined>> dicPrizeStore = new Dictionary<string, ConcurrentQueue<PrizeDefined>>(); PrizeDefined p = null; if (dicPrizeStore.ContainsKey(prize.Name)) { if (!string.IsNullOrEmpty(prize.SKU)) { dicPrizeStore[prize.Name].TryPeek(out p); } else { //有唯一标识的奖品 dicPrizeStore[prize.Name].TryDequeue(out p); } }
也很适用于相同资源只收取一个的场景,如后台防止多次提交
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
private static ConcurrentDictionary<long, DateTime> dic = new ConcurrentDictionary<long, DateTime>(); //防止http多次请求 if (!dic.TryAdd(DxUserToken.Instance.CICID, DateTime.Now)) { return false; } try {} catch{} finally { DateTime d; dic.TryRemove(DxUserToken.Instance.CICID, out d); }
三、Try Avoid Sync
锁会降低程序并发性,设计时尽量避免同步。如Xman只有一个Points属性,DB中直接作为user Participate表的一个列,用户玩一次,insert一条。而Xman自增线程update固定的一条非用户记录。这样避免了锁的存在。