1

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: コードの並列化

4

0 に答える 0