[C#]順序性を守ったロック

C# のいわゆる lock は、ロックがなされることを保証はしてくれるのですが、複数の待ちスレッドが発生した場合に順序性が担保されません。

どうにかして順序性を確保できないかなぁと思ったのですが...

結論から言うと、ロックだけで順序性を担保することはできないのであります。

だってロックしたらスレッドを消費してしまうから。

スレッドが消費されると未実行の Task の割り当ては生成時間順ではないから。

以下のコードは、スレッドプールに空きがあるうちは、確かに呼び出した順番に処理される可能性は高いのですが、Task.Factory.StartNew() とかでたくさんの処理が予約されたりしたならば、結果的にスレッドの割り付け自体がランダムなので、ロックを要求するタイミングはランダムになってしまうわけで、Taskそのものをキューに登録して順番に実行する以外に、確実な順序性処理はできないや、ということになりました。

通常の lock() に比べれば、タイムアウト処理とか書けるのですけれども、ある程度の順序性を守れてタイムアウト管理したいのであれば、ReaderWriterLock でいいやんけ、というオチに...

ThreadPool.SetMinThreads メソッドで、かなり大きな値にしてしまえば事実上は...いやいや確実性がないですね。

// タイムアウトの実装がまだ。タイムアウト監視スレッドを OrderingLockManager で回せばいい。
// 1000[ms]単位でじゅうぶん。
 
using System;
using System.Collections.Generic;
using System.Threading;
 
namespace Lock
{
    /// <summary>
    /// 
    /// </summary>
    public class OrderingLockInstance
    {
        /// <summary>
        /// 
        /// </summary>
        protected string Identifier { getset; }
    }
 
    /// <summary>
    /// 
    /// </summary>
    internal class OrderingLockManager
    {
        /// <summary>
        /// 
        /// </summary>
        static protected Dictionary<OrderingLockInstanceQueue<OrderingLock>> lockQueue = new Dictionary<OrderingLockInstanceQueue<OrderingLock>>();
 
        /// <summary>
        /// 
        /// </summary>
        static protected OrderingLockManager instance = null;
 
        /// <summary>
        /// 
        /// </summary>
        public static OrderingLockManager Instance
        {
            get
            {
                if(instance == null)
                {
                    lock(lockQueue)
                    {
                        if(instance == null)
                        {
                            instance = new OrderingLockManager();
                        }
                    }
                }
                return instance;
            }
        }
 
        /// <summary>
        /// 
        /// </summary>
        /// <param name="locker"></param>
        /// <param name="lockInstance"></param>
        public void EnQueue(OrderingLock locker, OrderingLockInstance lockInstance)
        {
            lock (lockQueue)
            {
                if(lockQueue.ContainsKey(lockInstance) == true)
                {
                    // 誰か忙しい人がいる
                    lockQueue[lockInstance].Enqueue(locker);
                }
                else
                {
                    lockQueue.Add(lockInstance, new Queue<OrderingLock>());
 
                    // 走りだせ!
                    locker.Ready.Set();
                }
            }
        }
 
        /// <summary>
        /// 
        /// </summary>
        /// <param name="locker"></param>
        /// <param name="lockInstance"></param>
        public void Finished(OrderingLock locker, OrderingLockInstance lockInstance)
        {
            lock (lockQueue)
            {
                if (lockQueue.ContainsKey(lockInstance) == true)
                {
                    if(lockQueue[lockInstance].Count == 0)
                    {
                        // おわり。
                        lockQueue.Remove(lockInstance);
                    }
                    else
                    {
                        // まだだ、まだ終わらんよ!
                        lockQueue[lockInstance].Dequeue().Ready.Set();
                    }
                }
                else
                {
                    // ERROR!(あってはならないルート)
                    throw new InvalidOperationException("Method calling sequence error.");
                }
            }
        }
 
        /// <summary>
        /// 
        /// </summary>
        protected OrderingLockManager()
        {
            // NOP
        }
    }
 
    /// <summary>
    /// 
    /// </summary>
    /// <example>
    /// このクラスの一般的な利用方法を次の例に示します。
    /// <code><![CDATA[
    /// OrderingLockInstance lockInstance = new OrderingLockInstance();
    ///
    /// ...
    /// 
    /// using (new OrderingLock(lockInstance))
    /// {
    ///     // Do something
    /// }
    /// ]]></code>
    /// </example>
    public class OrderingLock : IDisposable
    {
        /// <summary>
        /// 
        /// </summary>
        protected AutoResetEvent ready = new AutoResetEvent(false);
 
        /// <summary>
        /// 
        /// </summary>
        protected OrderingLockInstance lockInstance = null;
 
        /// <summary>
        /// 
        /// </summary>
        internal AutoResetEvent Ready
        {
            get
            {
                return ready;
            }
        }
 
        /// <summary>
        /// 
        /// </summary>
        /// <param name="lockInstance"></param>
        public OrderingLock(OrderingLockInstance lockInstance)
        {
            this.lockInstance = lockInstance;
            OrderingLockManager.Instance.EnQueue(this, lockInstance);
 
            ready.WaitOne();
        }
 
        /// <summary>
        /// 
        /// </summary>
        ~OrderingLock()
        {
            // using パターンを使わない人のために
 
            // 次の人へ
            if (lockInstance != null)
            {
                OrderingLockManager.Instance.Finished(this, lockInstance);
                lockInstance = null;
            }
        }
 
        /// <summary>
        /// 
        /// </summary>
        public void Dispose()
        {
            // きっと呼んでくれる...!
 
            // 次の人へ
            if (lockInstance != null)
            {
                OrderingLockManager.Instance.Finished(this, lockInstance);
                lockInstance = null;
            }
        }
    }
}

トラックバック(0)

トラックバックURL: http://www.hondarer-soft.com/japan/log_cgi/mt-tb.cgi/294

コメントする

ナビゲーション

プロフィール

Photo Hondarer  My status

自分に正直に、目指す物を目指すかたちで、全ての人が幸せになれるシステムを削り出す職人でありたい。

このブログ記事について

このページは、だらりんが2016年6月15日 22:45に書いたブログ記事です。

ひとつ前のブログ記事は「[C#]継承可能なシングルトンクラス」です。

最近のコンテンツはインデックスページで見られます。過去に書かれたものはアーカイブのページで見られます。

アーカイブ