6

TPL ランタイムにさまざまな優先度でタスクを追加する以前の作業はありますか?

そうでない場合、一般的に言えば、これをどのように実装しますか?

理想的には、プロデューサー/コンシューマー パターンを使用して「todo」作業を TPL に追加することを計画しています。優先度の低いジョブを (他のジョブに比べて) 優先度の高いジョブにアップグレードする必要があることに気付く場合があります。

これを検索するときに使用する必要がある検索キーワードを誰かが持っている場合は、それらに言及してください。必要なことを実行するコードをまだ見つけていないからです。

4

3 に答える 3

3

したがって、これはかなり単純な優先度キューに関するかなり単純な並行実装です。ここでの考え方は、実際のアイテムと優先度の両方のペアを保持するソートされたセットが存在するが、優先度のみを比較する比較子が与えられるということです。コンストラクターは、特定のオブジェクトの優先度を計算する関数を受け取ります。

実際の実装に関しては、それらは効率的に実装されているわけではありませんlock。より効率的な実装を作成するとSortedSet、 を優先キューとして使用できなくなります。同時に効果的にアクセスできるものの 1 つを再実装することは、それほど簡単ではありません。

アイテムの優先度を変更するには、セットからアイテムを削除してから再度追加する必要があります。セット全体を反復せずにアイテムを見つけるには、古い優先度と新しい優先度を知る必要があります。 .

public class ConcurrentPriorityQueue<T> : IProducerConsumerCollection<T>
{
    private object key = new object();
    private SortedSet<Tuple<T, int>> set;

    private Func<T, int> prioritySelector;

    public ConcurrentPriorityQueue(Func<T, int> prioritySelector, IComparer<T> comparer = null)
    {
        this.prioritySelector = prioritySelector;
        set = new SortedSet<Tuple<T, int>>(
            new MyComparer<T>(comparer ?? Comparer<T>.Default));
    }

    private class MyComparer<T> : IComparer<Tuple<T, int>>
    {
        private IComparer<T> comparer;
        public MyComparer(IComparer<T> comparer)
        {
            this.comparer = comparer;
        }
        public int Compare(Tuple<T, int> first, Tuple<T, int> second)
        {
            var returnValue = first.Item2.CompareTo(second.Item2);
            if (returnValue == 0)
                returnValue = comparer.Compare(first.Item1, second.Item1);
            return returnValue;
        }
    }

    public bool TryAdd(T item)
    {
        lock (key)
        {
            return set.Add(Tuple.Create(item, prioritySelector(item)));
        }
    }

    public bool TryTake(out T item)
    {
        lock (key)
        {
            if (set.Count > 0)
            {
                var first = set.First();
                item = first.Item1;
                return set.Remove(first);
            }
            else
            {
                item = default(T);
                return false;
            }
        }
    }

    public bool ChangePriority(T item, int oldPriority, int newPriority)
    {
        lock (key)
        {
            if (set.Remove(Tuple.Create(item, oldPriority)))
            {
                return set.Add(Tuple.Create(item, newPriority));
            }
            else
                return false;
        }
    }

    public bool ChangePriority(T item)
    {
        lock (key)
        {
            var result = set.FirstOrDefault(pair => object.Equals(pair.Item1, item));

            if (object.Equals(result.Item1, item))
            {
                return ChangePriority(item, result.Item2, prioritySelector(item));
            }
            else
            {
                return false;
            }
        }
    }

    public void CopyTo(T[] array, int index)
    {
        lock (key)
        {
            foreach (var item in set.Select(pair => pair.Item1))
            {
                array[index++] = item;
            }
        }
    }

    public T[] ToArray()
    {
        lock (key)
        {
            return set.Select(pair => pair.Item1).ToArray();
        }
    }

    public IEnumerator<T> GetEnumerator()
    {
        return ToArray().AsEnumerable().GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

    public void CopyTo(Array array, int index)
    {
        lock (key)
        {
            foreach (var item in set.Select(pair => pair.Item1))
            {
                array.SetValue(item, index++);
            }
        }
    }

    public int Count
    {
        get { lock (key) { return set.Count; } }
    }

    public bool IsSynchronized
    {
        get { return true; }
    }

    public object SyncRoot
    {
        get { return key; }
    }
}

上記のオブジェクトであるインスタンスを取得したらIProducerConsumerCollection<T>、それを の内部バッキング オブジェクトとして使用して、BlockingCollection<T>ユーザー インターフェイスを使いやすくすることができます。

于 2013-02-18T17:08:51.377 に答える
3

ParallelExtensionsExtras にはTaskScheduler、直接または独自のスケジューラのベースとして役立ついくつかのカスタム が含まれています。

具体的には、興味深いスケジューラーが 2 つあります。

  • QueuedTaskScheduler、異なる優先度で をスケジュールできますがTask、エンキューされた の優先度を変更することはできませんTask
  • ReprioritizableTaskScheduler、異なる優先度はありませんが、特定Taskのものをキューの前または後ろに移動できます。(優先度の変更は、現在待機中の s の数で O(n)ですが、同時にTask多くの s がある場合は問題になる可能性があります。)Task
于 2013-02-18T16:55:17.443 に答える
2

プライオリティ FIFO タスク キューを実装しました。
これは非常にシンプルで、優先度のある ActionBlock のように機能します。

https://github.com/ido-ran/PriorityTaskQueueで確認してください

于 2013-08-23T07:04:23.113 に答える