0

現在、プロジェクトに .NET 3.5 を使用しています。あるバックグラウンド スレッドによってキューに入れられ、さらに別のスレッドによってキューから取り出されたアイテムを取得する静的キューを実装する必要があります。.NET 3.5 には ObservableQueue などがないため、独自の Queue を派生させて INotifyCollectionChanged を実装しようとしました。これは、UI が Queue の内容をユーザーに表示する必要があるためです。

しかし、実行しようとすると、最初のバックグラウンド ワーカーがアイテムをキューに入れ、CollectionChanged が発生し、次のような例外が発生します。

c# 別のスレッドが所有しているため、呼び出し元のスレッドはこのオブジェクトにアクセスできません

ビジネス オブジェクト (キュー内のもの) はすべて INotifyPropertyChanged を実装し、それらをデキューするバックグラウンド ワーカーも一部のプロパティを変更するため、同じスレッドが CollectionChanged と共に PropertyChanged も呼び出します。奇妙なことに、PropertyChanged が発生したときにエラーは発生しませんが、CollectionChanged がクラッシュします。

4

5 に答える 5

0

非ディスパッチャ(つまり非GUI)スレッドからGUIを操作しようとすると、このような例外が発生します。あなたの場合、GUIスレッドであるかどうかをチェックせずにCollectionChangedイベントを発生させていると思います。この問題を解決するには、chrisendymionが書いたように、私たちがすべきですDispatcher。ここに2つの可能なオプションがあります。

1)カスタムキューへのすべての呼び出し(追加/削除)はディスパッチャーで行われます(Dispatcher.Invoke

2)CollectionChangedイベントを同じに発生させるコードをラップしますDispatcher.Invoke

お役に立てれば。

于 2012-08-27T13:03:47.760 に答える
0

まず、最初からINotifyCollectionChangedコレクションを実装する代わりにObservableCollectionを拡張する必要があります。また、マルチスレッドコレクションについては、このクラスを参照してください。これは、役立つ場合があります(マルチスレッドでの作業の場合のみ、キューの動作を追加する場合は、それを実装する必要があります)

パブリッククラスThreadSafeObservableCollection:ObservableCollection{プライベートSynchronizationContextSynchronizationContext;

    public ThreadSafeObservableCollection()
    {
        SynchronizationContext = SynchronizationContext.Current;

        // current synchronization context will be null if we're not in UI Thread
        if (SynchronizationContext == null)
            throw new InvalidOperationException("This collection must be instantiated from UI Thread, if not, you have to pass SynchronizationContext to con                                structor.");
    }

    public ThreadSafeObservableCollection(SynchronizationContext synchronizationContext)
    {
        if (synchronizationContext == null)
            throw new ArgumentNullException("synchronizationContext");

        this.SynchronizationContext = synchronizationContext;
    }

    protected override void ClearItems()
    {
        this.SynchronizationContext.Send(new SendOrPostCallback((param) => base.ClearItems()), null);
    }

    protected override void InsertItem(int index, T item)
    {
        this.SynchronizationContext.Send(new SendOrPostCallback((param) => base.InsertItem(index, item)), null);
    }

    protected override void RemoveItem(int index)
    {
        this.SynchronizationContext.Send(new SendOrPostCallback((param) => base.RemoveItem(index)), null);
    }

    protected override void SetItem(int index, T item)
    {
        this.SynchronizationContext.Send(new SendOrPostCallback((param) => base.SetItem(index, item)), null);
    }

    protected override void MoveItem(int oldIndex, int newIndex)
    {
        this.SynchronizationContext.Send(new SendOrPostCallback((param) => base.MoveItem(oldIndex, newIndex)), null);
    }
}
于 2012-10-03T19:28:28.877 に答える
0

次のようなものを使用します。

if (System.Windows.Application.Current.Dispatcher.CheckAccess())
        {
            Messages.Add(message);
        }
        else
        {
            System.Windows.Application.Current.Dispatcher.Invoke(DispatcherPriority.Normal,
                new Action(() => Messages.Add(message)));
        }

GUIがコレクションにバインドされている可能性があるため、GUIスレッドからのみアクセスできます。そして、GUIで動作する多くのスレッドは、結果としてエラーを受け取ることが多いため、それほど良いことではありません。

だから考えてみてください:なぜあなたはこのようなことをすることができないのですか:別のスレッドからのlistBox1.Items.Add(...)。これで、別のスレッドからリストにアクセスできない理由がわかりました。

(なぜlistBox1.Items.Add(...)googleのようなことができるのか本当にわからない場合は、たくさんの記事が見つかります

于 2012-08-27T14:02:30.613 に答える
0

これが機能しない理由については、誰もが優れた技術的な答えを持っています。現在のスレッドに割り当てられている「Dispatcher」オブジェクトには、UI へのアクセス権がありません。

.NET では、ディスパッチャーが現在のスレッドに存在しない場合、.NET は別のディスパッチャーを新たに作成しますが、これは、アクセスを同期する必要がある UI スレッドではなく、バックグラウンド スレッドと同じ同期コンテキストを持ちます。

私のお金のための最も簡単な解決策は、ObservableCollection で元のディスパッチャへの参照を保持し、別の Dispatcher で基本クラス呼び出しをオーバーライドすることです。次のようなものが機能するはずです。

public class ObservableDispatcherCollection<T> : ObservableCollection<T> where T : class
    {
        private Dispatcher _dispatcher;

        public ObservableDispatcherCollection(Dispatcher dispatcher)
        {
            _dispatcher = dispatcher; 
        }

        public ObservableDispatcherCollection(Control parent)
        {
            _dispatcher = parent.Dispatcher;
        }

        protected override void     OnCollectionChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            _dispatcher.Invoke(new Action(() =>
            {
                base.OnCollectionChanged(e);
            }));
        }

        protected override void OnPropertyChanged(System.ComponentModel.PropertyChangedEventArgs e)
        {
            _dispatcher.Invoke(new Action(() =>
            {
                base.OnPropertyChanged(e);
            }));
        }
}
于 2013-10-15T08:30:44.193 に答える
0

バックグラウンド スレッドがキューを変更するときに委任しようとしましたか?

お気に入り :

if (Dispatcher.Thread != Thread.CurrentThread)
{
       Dispatcher.Invoke(new Action(delegate()
       {
           //Modify your collection
       }));
 }
于 2012-08-27T12:41:00.120 に答える