스레드 세이프 Collection View는 어디서 구할 수 있나요?
백그라운드 스레드에서 비즈니스 오브젝트 컬렉션을 업데이트하면 다음 오류 메시지가 나타납니다.
이 유형의 CollectionView는 Dispatcher 스레드와 다른 스레드에서 SourceCollection으로의 변경을 지원하지 않습니다.
그래, 말이 되네.그러나 Collection View의 어떤 버전이 여러 스레드를 지원하는지, 그리고 객체가 어떻게 이 스레드를 사용할 수 있는지에 대한 질문도 있습니다.
용도:
System.Windows.Application.Current.Dispatcher.Invoke(
System.Windows.Threading.DispatcherPriority.Normal,
(Action)delegate()
{
// Your Action Code
});
다음은 Jonathan이 발견한 구현 개선 사항입니다.첫 번째로 모든 이벤트핸들러가 같은(UI) 디스패처 상에 있다고 가정하지 않고 연관된 디스패처 상에서 각 이벤트핸들러를 실행합니다.두 번째로 Begin을 사용합니다.호출하여 디스패처가 사용 가능해질 때까지 처리를 계속할 수 있도록 합니다.이것에 의해, 백그라운드 스레드가 각 스레드간의 처리로 많은 갱신을 실시하는 상황에서는, 솔루션의 고속화를 도모할 수 있습니다.아마도 더 중요한 것은 Invoke를 기다리는 동안 블로킹으로 인한 문제를 해결한다는 것입니다(예를 들어 Concurency Mode에서 WCF를 사용하는 경우 데드록이 발생할 수 있습니다).싱글)
public class MTObservableCollection<T> : ObservableCollection<T>
{
public override event NotifyCollectionChangedEventHandler CollectionChanged;
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
NotifyCollectionChangedEventHandler CollectionChanged = this.CollectionChanged;
if (CollectionChanged != null)
foreach (NotifyCollectionChangedEventHandler nh in CollectionChanged.GetInvocationList())
{
DispatcherObject dispObj = nh.Target as DispatcherObject;
if (dispObj != null)
{
Dispatcher dispatcher = dispObj.Dispatcher;
if (dispatcher != null && !dispatcher.CheckAccess())
{
dispatcher.BeginInvoke(
(Action)(() => nh.Invoke(this,
new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset))),
DispatcherPriority.DataBind);
continue;
}
}
nh.Invoke(this, e);
}
}
}
Begin을 사용하고 있기 때문에호출하면 핸들러가 호출되기 전에 변경 알림이 취소될 수 있습니다.이로 인해 일반적으로 이벤트 인수가 목록의 새로운(변경된) 상태와 대조될 때 "Index was out of range." 예외가 느려집니다.이를 방지하기 위해 지연된 모든 이벤트는 Reset 이벤트로 대체됩니다.이로 인해 과도한 그리기 작업이 발생할 수 있습니다.
Bea Stollnitz의 이 게시물은 오류 메시지와 왜 이 메시지가 그대로 표현되었는지 설명합니다.
편집: Bea 블로그에서
유감스럽게도 이 코드에서는 예외가 발생합니다.「 Not Supported 」예외 – 이런 유형의 CollectionView는 Dispatcher 스레드와 다른 스레드에서 SourceCollection으로의 변경을 지원하지 않습니다."이 에러 메세지는, 사용하고 있는 Collection View가 크로스 스레드 변경을 서포트하고 있지 않은 경우는, 그 변경을 서포트하고 있는 것을 찾아낼 필요가 있다고 생각하도록 유도합니다.이 에러 메시지는 조금 오해를 불러일으킵니다.즉, 개봉 즉시 제공되는 CollectionView는 크로스 스레드 컬렉션 변경을 지원하지 않습니다.아니요, 안타깝게도 현재 오류 메시지를 수정할 수 없습니다. 매우 많이 잠겨 있습니다.
하나 찾았어요.
public class MTObservableCollection<T> : ObservableCollection<T>
{
public override event NotifyCollectionChangedEventHandler CollectionChanged;
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
var eh = CollectionChanged;
if (eh != null)
{
Dispatcher dispatcher = (from NotifyCollectionChangedEventHandler nh in eh.GetInvocationList()
let dpo = nh.Target as DispatcherObject
where dpo != null
select dpo.Dispatcher).FirstOrDefault();
if (dispatcher != null && dispatcher.CheckAccess() == false)
{
dispatcher.Invoke(DispatcherPriority.DataBind, (Action)(() => OnCollectionChanged(e)));
}
else
{
foreach (NotifyCollectionChangedEventHandler nh in eh.GetInvocationList())
nh.Invoke(this, e);
}
}
}
}
http://www.julmar.com/blog/mark/2009/04/01/AddingToAnObservableCollectionFromABackgroundThread.aspx
다음 항목도 참조할 수 있습니다.BindingOperations.EnableCollectionSynchronization.
로의 업그레이드를 참조해 주세요.NET 4.5: ItemsControl이 해당 항목 소스와 일치하지 않습니다.
죄송합니다. 댓글을 추가할 수 없지만 이 모든 것은 잘못되었습니다.
ObservableCollection은 스레드 안전이 아닙니다.디스패처 문제뿐만 아니라 (msdn에서) 스레드 세이프가 전혀 아닙니다.
이 유형의 공용 정적(Visual Basic에서 공유) 구성원은 스레드 안전합니다.인스턴스 멤버가 스레드 세이프가 보장되지 않습니다.
http://msdn.microsoft.com/en-us/library/ms668604(v=vs.110).aspx 를 참조해 주세요.
Begin을 호출할 때도 문제가 있습니다."Reset" 작업으로 호출합니다."Reset"은 핸들러가 컬렉션 자체를 확인해야 하는 유일한 작업입니다.시작하는 경우「리셋」을 기동해, 곧바로 개시합니다.몇 가지 "추가" 작업을 호출하면 핸들러가 이미 업데이트된 컬렉션을 포함한 "재설정"을 수락하고 다음 "추가" 작업을 실행하면 혼란이 발생합니다.
이것이 효과적인 나의 구현입니다.사실 Begin을 삭제하려고 합니다.호출:
다음과 같이 수집 동기화를 활성화하면 컬렉션에 대한 크로스 스레드 변경을 wpf에서 관리할 수 있습니다.
BindingOperations.EnableCollectionSynchronization(collection, syncLock);
listBox.ItemsSource = collection;
이를 통해 WPF는 UI 스레드에서 컬렉션이 변경될 수 있으므로 UI 변경 내용을 적절한 스레드로 다시 수집해야 함을 알 수 있습니다.
잠금 개체가 없는 경우 동기화 콜백을 제공하기 위한 오버로드도 있습니다.
WPF UI Control을 정기적으로 업데이트하고 UI를 사용하는 경우 DispatcherTimer를 사용할 수 있습니다.
XAML
<Grid>
<DataGrid AutoGenerateColumns="True" Height="200" HorizontalAlignment="Left" Name="dgDownloads" VerticalAlignment="Top" Width="548" />
<Label Content="" Height="28" HorizontalAlignment="Left" Margin="0,221,0,0" Name="lblFileCouner" VerticalAlignment="Top" Width="173" />
</Grid>
C#
public partial class DownloadStats : Window
{
private MainWindow _parent;
DispatcherTimer timer = new DispatcherTimer();
ObservableCollection<FileView> fileViewList = new ObservableCollection<FileView>();
public DownloadStats(MainWindow parent)
{
InitializeComponent();
_parent = parent;
Owner = parent;
timer.Interval = new TimeSpan(0, 0, 1);
timer.Tick += new EventHandler(timer_Tick);
timer.Start();
}
void timer_Tick(object sender, EventArgs e)
{
dgDownloads.ItemsSource = null;
fileViewList.Clear();
if (_parent.contentManagerWorkArea.Count > 0)
{
foreach (var item in _parent.contentManagerWorkArea)
{
FileView nf = item.Value.FileView;
fileViewList.Add(nf);
}
}
if (fileViewList.Count > 0)
{
lblFileCouner.Content = fileViewList.Count;
dgDownloads.ItemsSource = fileViewList;
}
}
}
시험:
this.Dispatcher.Invoke(DispatcherPriority.Background, new Action(
() =>
{
//Code
}));
그들 중 아무도, 그냥 디스패처를 사용해.BeginInvoke
여기 제가 구글 검색과 약간의 수정을 거쳐 만든 VB 버전이 있습니다.저는 좋아요.
Imports System.Collections.ObjectModel
Imports System.Collections.Specialized
Imports System.ComponentModel
Imports System.Reflection
Imports System.Windows.Threading
'from: http://stackoverflow.com/questions/2137769/where-do-i-get-a-thread-safe-collectionview
Public Class ThreadSafeObservableCollection(Of T)
Inherits ObservableCollection(Of T)
'from: http://geekswithblogs.net/NewThingsILearned/archive/2008/01/16/listcollectionviewcollectionview-doesnt-support-notifycollectionchanged-with-multiple-items.aspx
Protected Overrides Sub OnCollectionChanged(ByVal e As System.Collections.Specialized.NotifyCollectionChangedEventArgs)
Dim doit As Boolean = False
doit = (e.NewItems IsNot Nothing) AndAlso (e.NewItems.Count > 0)
doit = doit OrElse ((e.OldItems IsNot Nothing) AndAlso (e.OldItems.Count > 0))
If (doit) Then
Dim handler As NotifyCollectionChangedEventHandler = GetType(ObservableCollection(Of T)).GetField("CollectionChanged", BindingFlags.Instance Or BindingFlags.NonPublic).GetValue(Me)
If (handler Is Nothing) Then
Return
End If
For Each invocation As NotifyCollectionChangedEventHandler In handler.GetInvocationList
Dim obj As DispatcherObject = invocation.Target
If (obj IsNot Nothing) Then
Dim disp As Dispatcher = obj.Dispatcher
If (disp IsNot Nothing AndAlso Not (disp.CheckAccess())) Then
disp.BeginInvoke(
Sub()
invocation.Invoke(Me, New NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset))
End Sub, DispatcherPriority.DataBind)
Continue For
End If
End If
invocation.Invoke(Me, e)
Next
End If
End Sub
End Class
VB 버전에서는 작은 오류가 발생했습니다.대체:
Dim obj As DispatcherObject = invocation.Target
타고
Dim obj As DispatcherObject = TryCast(invocation.Target, DispatcherObject)
언급URL : https://stackoverflow.com/questions/2137769/where-do-i-get-a-thread-safe-collectionview
'programing' 카테고리의 다른 글
| Bash 구문 오류: 예기치 않은 파일 끝 (0) | 2023.04.21 |
|---|---|
| 개체 ID를 가진 클라이언트에 'Microsoft' 작업을 수행할 수 있는 권한이 없습니다.Data Factory / 데이터 팩토리 / 데이터 라인 / 읽기' 범위 초과 (0) | 2023.04.21 |
| Excel에서 웹 페이지로 복사/붙여넣기 (0) | 2023.04.21 |
| 실행 시 WPF 버튼의 이미지가 표시되지 않음 (0) | 2023.04.21 |
| azure brocredmessage는 타입을 모른 채 몸을 구한다. (0) | 2023.04.21 |