2016年1月19日 星期二

Async Await DeadLock問題


之前寫了一個簡單上傳圖片的功能,碰到用await跟async非同執行的問題,本來在本機Visual Studio偵錯執行Run都好好的,但是放到線上後就永遠得不到回應TimeOut

隱隱約約記得之前黑大寫過類似的文章,當時看過沒啥特別感覺,翻回來看後在實際操作後終於弄懂了(果然寫程式很多地方一知半解會GG的),關於Async、Await這些推薦可以看這兩篇文章

Huan-Lin 學習筆記-async 與 await
黑暗執行緒- await與Task.Result/Task.Wait()的Deadlock問題

兩位前輩大大已經寫的很完整詳盡了,在這邊就不獻醜了,只簡單提供個範例給有興趣的人玩玩


public class DeadLockController : ApiController
    {
        [HttpGet]
        [Route("api/DeadLock/Index")]
        public HttpResponseMessage Index()
        {
            Task<int> mytask = Go();
            mytask.Wait();
            return Request.CreateResponse(mytask.Result);
        }

        [HttpGet]
        [Route("api/DeadLock")]
        public HttpResponseMessage DeadLock()
        {
            var result = Go().Result;
            return Request.CreateResponse(result);
        }

        [HttpGet]
        [Route("api/DeadLock/NoDeadLock")]
        public async Task<HttpResponseMessage> NoDeadLock()
        {
            var result = await Go();
            return Request.CreateResponse(result);
        }


        private async Task<int> Go()
        {
            int res = 0;
            await Task.Run(() =>
            {
                //假裝執行某個耗時程序後取得結果
                Task.Delay(1000);
                res = 32767;
            });
            return res;
        }
    }

這邊只有NoDeadLock那個Action的寫法才不會DeadLock,其餘兩個永遠都不會得到回應,原因簡單的說就是在Web中(特別強調在Web中,因為Console並不會),.Result 與 Wait()的方法都會鎖定目前的Thread等待結果回應再繼續執行,而上面的範例GO()這個Method在Web中會記住是哪個Thread來呼叫他的,以便他執行完後回應該Thread結果繼續執行。

這時候就會發生DeadLock了,因為外面用Wait() or .Result鎖住當前Thread,GO()這個Method又在等待他被釋放好跟他回應結果,互相等待也就DeadLock了。

前面兩篇文章很久以前就看過了,只是當時沒有特別放在心上,直到被這個簡單到只有5行Code的東西困擾了3~4天,才終於想到有這件事情,現在不懂沒關係,等到被鎖一次就懂了(吶喊!!)

2016年1月10日 星期日

[Linq]動態組合Linq條件


以前寫Linq的時候常常碰到一個問題,就是判斷一個條件是否達成來決定Linq的Where條件該如何組合,
例如:

void Main()
{
 bool Flag = true;
 List<DealDataForGov> SourceData = new List<DealDataForGov>
 {
  new DealDataForGov
  {
   Address = "台北市xxxx",
   HouseKind = "a1",
   Year = 2015
  },
  new DealDataForGov
  {
   Address = "高雄市xxxx",
   HouseKind = "a1",
   Year = 2016
  },
  new DealDataForGov
  {
   Address = "台中市xxxx",
   HouseKind = "c1",
   Year = 2014
  }
 }
        
        
 if (Flag)
 {
  SourceData.Where((x) => x.HouseKind == "a1");
 }
 else 
 {
  //因為某個條件,新增新的搜尋條件
  SourceData.Where((x) => x.HouseKind =="a1" && x.Year >= 2015 );
 }
}

public class DealDataForGov
{
 public string Address { get; set; }
 public string HouseKind { get; set; }
 public int Year {get;set;}
}

條件少還算好寫,一旦條件落落長的時候就會貼的到處都是,不僅彈性降低也讓維護增加困難度,還好在黑大那邊看到LINQKIT這個好東西黑暗執行緒-組裝動態LINQ條件的利器-LINQKit

以下簡短介紹一下這個LINQKit的套件,有了他之後感覺Code都活過來了XD
範例:

void Main()
{
 List<TargetModel> SourceData = new List<TargetModel>
 {
  new TargetModel
  {
   Address = "台北市xxxx",
   HouseKind = "a1",
   Year = 2015
  },
  new TargetModel
  {
   Address = "高雄市xxxx",
   HouseKind = "a1",
   Year = 2016
  },
  new TargetModel
  {
   Address = "台中市xxxx",
   HouseKind = "c1",
   Year = 2014
  }
 };
 
 
 var expression = GetWhereExpression();

 //SourceData.Where((x) => x.HouseKind == "a1");
 var e1 = SourceData.Where(expression.Compile());
 e1.Dump();

 //SourceData.Where((x) => x.HouseKind =="a1" && x.Year > 2015 );
 var e2 = SourceData.Where(expression.And(x => x.Year > 2015).Compile());
 e2.Dump();

 //SourceData.Where((x) => x.HouseKind =="a1" || x.Year >=2014 );
 var e3 = SourceData.Where(expression.Or(x => x.Year >= 2014).Compile());
 e3.Dump();
}

public Expression<Func<TargetModel, bool>> GetWhereExpression()
{
 var pred = PredicateBuilder.True<TargetModel>();
  pred = pred.And(x=> x.HouseKind =="a1");
  return pred;
}

public class TargetModel
{
 public string Address { get; set; }
 public string HouseKind { get; set; }
 public int Year {get;set;}
}


上面有三個範例分別是替代掉註解的寫法,如果有碰到很多條件會動態產生的結果,用LinqKit可以大幅度省掉很多Code以及維護上的複雜度。算是非常好用的小物,推薦給大家