2009-11-21 147 views
0
var arguments = new double[] { 1d, 2d, 3d }; 
var result = arguments.Select(arg => Math.Sqrt(arg)); 

現在想象一下,一個異步方法,而不是Math.Sqrt(我不知道下面的方法是一個真正的async方法,但它的行爲幾乎相當於一個)如何同步異步方法?

public void BeginSqrt(Action<double> callback, double argument) 
{ 
    Thread.Sleep(100); 
    callback(Math.Sqrt(argument)); 
} 

有稱這樣的不正確的方式方法而不分割代碼。所以讓我們將這個異步方法與AutoResetEvent同步。我創建了一個輔助類:

public class Synchronizer<T, TResult> 
{ 
    AutoResetEvent _autoResetEvent = new AutoResetEvent(false); 
    TResult _result; 

    public TResult Execute(Action<Action<TResult>,T> beginMethod, T argument) 
    { 
     beginMethod(Callback, argument); 
     _autoResetEvent.WaitOne(); 
     return _result; 
    } 

    void Callback(TResult result) 
    { 
     _result = result; 
     _autoResetEvent.Set(); 
    } 
} 

有了這個類,我們可以:

var synchronizer = new Synchronizer<double, double>(); 
var result = arguments.Select(arg => synchronizer.Execute(BeginSqrt, arg)); 

這種解決方案我在幾分鐘內創建的,而我思考的問題。有一個本地的替代呢?我相信我的解決方案存在缺陷,因爲它錯過了一些鎖。有一個更可靠的圖書館來做到這一點?

+0

如果我正確地讀取了你的代碼,'BeginSqrt'完全不會做任何異步操作。它只是將返回值傳遞給回調函數而不是返回值(「continuation passing style」)。你的'Synchronizer'基本上是一個將'BeginSqrt'變成常規方法的包裝器。沒有涉及到併發,所以不需要鎖。 – dtb 2009-11-21 18:05:24

+0

@dtb您說得對,也許我應該將其更改爲一個真正的異步方法以進行澄清 – 2009-11-21 18:15:44

回答

0

使用Parallel LINQ你可以寫:

var arguments = new double[] { 1d, 2d, 3d }; 
var result = arguments.AsParallel().Select(arg => Math.Sqrt(arg)); 

這將計算並行每個參數的平方根。 這就是你想要達到的目標嗎?

+0

您的解決方案將產生與我試圖在此特定情況下實現的結果類似的結果。但想象一下,我試圖使用像HttpWebRequest.BeginGetResponse這樣的真正的異步方法。在這種情況下,您的解決方案只能在雙核機器上同時執行兩個請求,而我試圖實現類似30個同時請求的操作。 – 2009-11-21 18:12:50

+0

AFAIK PLINQ使用的線程數量會動態調整爲最適合您的機器:它不固定於您的處理器/內核的數量,這很愚蠢。 – dtb 2009-11-21 18:18:20

+0

您錯過了這一點,請注意數量級:2 x 30.儘管有些請求是出站的,但新的請求可以開始。 – 2009-11-21 18:20:16