LoginSignup
6
6

More than 5 years have passed since last update.

マルチスレッドで複数のコレクション操作を排他制御して処理したかった

Posted at

いろんなスレッドからObservableCollectionを操作する時コレクション操作するとDispatcher.Invokeとかで書かなきゃいけないので
http://okazuki.hatenablog.com/entry/20100112/1263267397
な感じでやったりしてました。

けど開発を進めていくと、一連の処理をロックした状態で行いたいケースが結構あるんですよね。
あとは重いコレクション操作処理をDispathcerスレッドで行いたくないときとか。
変更と削除と追加を一つの処理として行いたいとか。
lock(hogelock)
{
// 変更処理
// 削除処理
// 追加処理
}
とか書けばよいんですけど、ロックオブジェクトの管理が・・・・

いろいろなスレッドからコレクション操作したい。
複数のコレクション操作にてデータの整合性を確保したい。
という要件を満たすための付け焼刃です。


    public class TransactionDispatchObservableCollection<T> : ObservableCollection<T>
    {
        /// <summary>
        /// コレクションディスパッチャー
        /// </summary>
        public Dispatcher Dispatch { get; set; }

        public DispatcherPriority Priority { get; set; } = DispatcherPriority.Background;

        private object TransactLock = new object();

        #region コンストラクタ

        public TransactionDispatchObservableCollection(Dispatcher Dispatch)
        {
            this.Dispatch = Dispatch;
        }
        public TransactionDispatchObservableCollection(Dispatcher Dispatch, IEnumerable<T> collection) : base(collection)
        {
            this.Dispatch = Dispatch;
        }

        #endregion

        /// <summary>
        /// 関連処理を排他制御しながら実行します。
        /// </summary>
        /// <param name="action"></param>
        public void TransactExcution(Action action)
        {
            lock (TransactLock)
            {
                action();
            }
        }
        /// <summary>
        /// 関連処理を排他制御しながら実行します。(非同期)
        /// </summary>
        public async Task TransactExcutionAsync(Action action)
        {
            await Task.Run(() => {
                lock (TransactLock)
                {
                    action();
                }
            });
        }
        protected override void ClearItems()
        {
            TransactExcution(() => base.ClearItems());
        }
        protected override void InsertItem(int index, T item)
        {
            TransactExcution(() => base.InsertItem(index, item));
        }
        protected override void MoveItem(int oldIndex, int newIndex)
        {
            TransactExcution(() => base.MoveItem(oldIndex, newIndex));
        }
        protected override void SetItem(int index, T item)
        {
            TransactExcution(() => base.SetItem(index, item)); 
        }
        protected override void RemoveItem(int index)
        {
            TransactExcution(() => base.RemoveItem(index));
        }

        protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
        {
            if (this.Dispatch.Thread == Thread.CurrentThread)
            {
                base.OnCollectionChanged(e);
            }
            else
            {
                Action<NotifyCollectionChangedEventArgs> changed = OnCollectionChanged;
                this.Dispatch.Invoke(changed, Priority, e);
            }
        }
    }

こんな感じで


await list.TransactExcutionAsync(() =>
                {
                    if (!list.Any((x => x.Name == "Test")))
                    {
                        list.Add(model);
                    }
                });

色々ダメなんですが限定用とであれば使えるかなぁと。

6
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
6