ThreadPool.QueueUserWorkItem を TPL にリファクタリングしようとしていますが、正直なところ、どちらもよくわかりません (まだ)。
実際には、これは I/O 操作になります。PartitionKey で Azure Table Storage にクエリを実行して、効果的に並列化できるようにします。
メソッド GetAllTPL() を参照してください。これには、私が書いた単純な TPL が含まれています。この非常に単純なテスト ケースに合格します。TPL は間違っているのですか? 私はそれをもっとうまくやることができますか?実際のテーブル クエリの場合、この簡単なテスト ケースが失敗する可能性があるものはありますか?
ThreadPool.QueueUserWorkItem と TPL はどちらも、この非常に限定されたテスト ケースに対して同じ正解を生成します。より大きな (ただし単純化された) テスト ケースにストップウォッチを配置すると、TPL は ThreadPool.QueueUserWorkItem の 2 倍の速度に達しません。TPL は 3 つのグループでキューに入れられ、ThreadPool.QueueUserWorkItem は 2 つのグループで、ハイパー スレッディングを備えた P4 だけで実行されているようです。thread.sleep は実際の作業ではないため、多くのことを教えてくれません。
ThreadPool.QueueUserWorkItem は、.NET 3.5 コード サンプル (TPL 以前) からのものです。TPLの方が優れたオプションがあると確信していますが、TPLは初めてです。ありがとう
using System.Threading;
using System.Threading.Tasks;
namespace WaitForConsole
{
class Program
{
static void Main(string[] args)
{
string[] partitionKey = new string[] { "one", "two", "three", "four" };
IEnumerable<string> commonRowKeys = GetAllQWI(partitionKey);
foreach (string commonRowKey in commonRowKeys) Console.WriteLine(commonRowKey);
Console.ReadLine();
commonRowKeys = GetAllTPL(partitionKey);
foreach (string commonRowKey in commonRowKeys) Console.WriteLine(commonRowKey);
Console.ReadLine();
}
public static IEnumerable<string> GetAllQWI(string[] partitionKey)
{
// this a a code sample from .NET 3.5 and it runs on 4.0
IEnumerable<string> finalResults = null;
ManualResetEvent[] resetEvents = new ManualResetEvent[partitionKey.Length];
HashSet<string>[] rowKeys = new HashSet<string>[partitionKey.Length];
for (int i = 0; i < rowKeys.Length; i++)
{
resetEvents[i] = new ManualResetEvent(false);
ThreadPool.QueueUserWorkItem(new WaitCallback((object index) =>
{
Console.WriteLine("GetAllQWI " + ((int)index).ToString());
rowKeys[(int)index] = TableQueryGetRowKeys(partitionKey[(int)index]);
resetEvents[(int)index].Set();
}), i);
}
try
{
WaitHandle.WaitAll(resetEvents);
Console.WriteLine("WaitAll done");
finalResults = (IEnumerable<string>)rowKeys[0];
foreach (var thisRowKeys in rowKeys)
{
finalResults = finalResults.Intersect(thisRowKeys);
}
}
catch (Exception ex)
{
Console.WriteLine("WaitAll ex " + ex.Message);
}
return finalResults;
}
public static IEnumerable<string> GetAllTPL(string[] partitionKey)
{
// this is the conversion of the ThreadPool.QueueUserWorkItem to TPL
// seems to be working but is this optimal
IEnumerable<string> finalResults = null;
HashSet<string>[] rowKeys = new HashSet<string>[partitionKey.Length];
// how to do this in TPL
Parallel.For(0, partitionKey.Length, i =>
{
Console.WriteLine("GetAllTPL " + i.ToString());
rowKeys[i] = TableQueryGetRowKeys(partitionKey[i]);
});
// Do I need to do anything special to wait for all the tasks to finish?
// Interesting that i is not necessarily in order but it does not need to be
finalResults = (IEnumerable<string>)rowKeys[0];
foreach (var thisRowKeys in rowKeys)
{
finalResults = finalResults.Intersect(thisRowKeys);
}
return finalResults;
}
public static HashSet<string> TableQueryGetRowKeys(string partitionKey)
{
// in real life this is an Azure table query to get all rowKeys for a partitionKey
Thread.Sleep(10000);
if (DateTime.Now.Millisecond % 2 == 0)
{
return new HashSet<string> { "alph", "beta", "gamma", "delta" };
}
else
{
return new HashSet<string> { "beta", "gamma", "delta", "epsilon" };
}
}
}
}
ここまで来るのに数時間を費やしました。TPL がこれを行う場合、ThreadPool.QueueUserWorkItem を学習するのではなく、TPL について詳しく学習します。TPLがとても単純だったのはちょっと怖いです。何かが欠けていないことを確認したいだけです。この TPL は、計算集約型のサンプルから入手しました。
TPL と Azure での不規則な結果について SO に 1 つの投稿がありましたが、最終的には再現できませんでした。 CloudTableQuery で Parallel.ForEach を安全に使用できますか
Azure チームからの別の投稿では、TPL と Azure は問題ないと述べています Windows Azure: コードの並列化