使自定义 C# 集合线程安全
Make custom C# collection thread-safe
我创建了这个特定的集合 class :
public class RangeObservableCollection<T> : ObservableCollection<T>
{
private bool _suppressNotification = false;
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
try
{
if (!_suppressNotification)
base.OnCollectionChanged(e);
}
catch
{
}
}
public void AddRange(IEnumerable<T> list)
{
if (list == null) {
return ;
}
_suppressNotification = true;
foreach (T item in list)
{
Add(item);
}
_suppressNotification = false;
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
public void EditElement(int index, object value)
{
_suppressNotification = true;
if (index >= 0 && index < this.Count) this[index] = (T)value;
_suppressNotification = false;
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
public void RemoveElementAt(int index)
{
_suppressNotification = true;
if (index >= 0 && index < this.Count) this.RemoveAt(index);
_suppressNotification = false;
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
public void RemoveElement(T element)
{
_suppressNotification = true;
this.Remove(element);
_suppressNotification = false;
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
}
那么我想这样使用它:
public RangeObservableCollection<vue_medecin > MedecinGDP { get; set; }
public RangeObservableCollection<gdp_groupe > CodeGDP_Collection { get; set; }
public RangeObservableCollection<fsign_fiche > FiltredParticipant { get; set; }
private async Task FillList()
{
await Task.Factory.StartNew(() =>
{
gdpList = SimpleIoc.Default.GetInstance<ICrud<gdp_groupe>>().GetAll().ToList();
MedecinGDP.AddRange(SimpleIoc.Default.GetInstance<ICrud<vue_medecin>>().GetAll());
CodeGDP_Collection.AddRange(gdpList);
FiltredParticipant.AddRange(SimpleIoc.Default.GetInstance<ICrud<fsign_fiche>>().GetAll());
});
}
我想自定义 RangeObservableCollection
使其成为线程安全的,因为在我的例子中,主线程和任务之间的并发访问可能会导致问题。
我想避免使用并发集合(如回答),因为我必须在我的程序中使用这种集合类型。
那么,我该如何编辑这个实现来完成这个任务?最好的主意是什么?
谢谢,
我一般用
private readonly object m_lock = new object();
每次访问您的方法时,请执行以下操作:
lock(m_lock){
// your code goes here
}
使用它可以确保一次只有一个线程可以访问这段代码。希望这有帮助
您不能使此自定义集合线程安全,因为它继承自非线程安全的 class。您也许可以使某些方法线程安全(您可以覆盖的方法或您自己编写的方法)但不是所有方法(并非基 class 的所有成员都是虚拟的)。
如果您希望创建一个线程安全的自定义集合,您应该继承 System.Collections.Concurrent 命名空间中的 classes 之一。
另一种选择是使用容器方法而不是继承,例如写一个线程安全的包装器而不是后代 class.
如果我没理解错的话,你只有一个线程在修改集合,只是想确保 reader 可以安全地访问它。
如果是这样,您可以让后台线程计算新元素,但分派将元素添加到 reader 线程。这有效地使 mutating/reading 都发生在同一个线程上,避免了同步问题。我相信这就是 Servy 在评论中提出的建议。
如果您从 UI 线程读取,这是最简单的,因为您可以使用标准调度机制。 Async/await 使这特别容易(假设从 UI 线程调用 FillList):
private async Task FillList()
{
gdpList = await Task.Run(() => SimpleIoc.Default.GetInstance<ICrud<gdp_groupe>>().GetAll().ToList());
MedecinGDP.AddRange(await Task.Run(() => SimpleIoc.Default.GetInstance<ICrud<vue_medecin>>().GetAll()));
CodeGDP_Collection.AddRange(gdpList);
FiltredParticipant.AddRange(await Task.Run(() => SimpleIoc.Default.GetInstance<ICrud<fsign_fiche>>().GetAll()));
}
我创建了这个特定的集合 class :
public class RangeObservableCollection<T> : ObservableCollection<T>
{
private bool _suppressNotification = false;
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
try
{
if (!_suppressNotification)
base.OnCollectionChanged(e);
}
catch
{
}
}
public void AddRange(IEnumerable<T> list)
{
if (list == null) {
return ;
}
_suppressNotification = true;
foreach (T item in list)
{
Add(item);
}
_suppressNotification = false;
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
public void EditElement(int index, object value)
{
_suppressNotification = true;
if (index >= 0 && index < this.Count) this[index] = (T)value;
_suppressNotification = false;
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
public void RemoveElementAt(int index)
{
_suppressNotification = true;
if (index >= 0 && index < this.Count) this.RemoveAt(index);
_suppressNotification = false;
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
public void RemoveElement(T element)
{
_suppressNotification = true;
this.Remove(element);
_suppressNotification = false;
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
}
那么我想这样使用它:
public RangeObservableCollection<vue_medecin > MedecinGDP { get; set; }
public RangeObservableCollection<gdp_groupe > CodeGDP_Collection { get; set; }
public RangeObservableCollection<fsign_fiche > FiltredParticipant { get; set; }
private async Task FillList()
{
await Task.Factory.StartNew(() =>
{
gdpList = SimpleIoc.Default.GetInstance<ICrud<gdp_groupe>>().GetAll().ToList();
MedecinGDP.AddRange(SimpleIoc.Default.GetInstance<ICrud<vue_medecin>>().GetAll());
CodeGDP_Collection.AddRange(gdpList);
FiltredParticipant.AddRange(SimpleIoc.Default.GetInstance<ICrud<fsign_fiche>>().GetAll());
});
}
我想自定义 RangeObservableCollection
使其成为线程安全的,因为在我的例子中,主线程和任务之间的并发访问可能会导致问题。
我想避免使用并发集合(如回答
那么,我该如何编辑这个实现来完成这个任务?最好的主意是什么?
谢谢,
我一般用
private readonly object m_lock = new object();
每次访问您的方法时,请执行以下操作:
lock(m_lock){
// your code goes here
}
使用它可以确保一次只有一个线程可以访问这段代码。希望这有帮助
您不能使此自定义集合线程安全,因为它继承自非线程安全的 class。您也许可以使某些方法线程安全(您可以覆盖的方法或您自己编写的方法)但不是所有方法(并非基 class 的所有成员都是虚拟的)。
如果您希望创建一个线程安全的自定义集合,您应该继承 System.Collections.Concurrent 命名空间中的 classes 之一。
另一种选择是使用容器方法而不是继承,例如写一个线程安全的包装器而不是后代 class.
如果我没理解错的话,你只有一个线程在修改集合,只是想确保 reader 可以安全地访问它。
如果是这样,您可以让后台线程计算新元素,但分派将元素添加到 reader 线程。这有效地使 mutating/reading 都发生在同一个线程上,避免了同步问题。我相信这就是 Servy 在评论中提出的建议。
如果您从 UI 线程读取,这是最简单的,因为您可以使用标准调度机制。 Async/await 使这特别容易(假设从 UI 线程调用 FillList):
private async Task FillList()
{
gdpList = await Task.Run(() => SimpleIoc.Default.GetInstance<ICrud<gdp_groupe>>().GetAll().ToList());
MedecinGDP.AddRange(await Task.Run(() => SimpleIoc.Default.GetInstance<ICrud<vue_medecin>>().GetAll()));
CodeGDP_Collection.AddRange(gdpList);
FiltredParticipant.AddRange(await Task.Run(() => SimpleIoc.Default.GetInstance<ICrud<fsign_fiche>>().GetAll()));
}