我正在编写一个应用程序,该应用程序显示用户可以选择的对象列表,然后通过PropertyGrid控件查看和编辑属性。通过使用辅助线程从文件中提取信息的耗时过程来填充对象的属性。但是,我还希望允许用户在提取过程正在进行时继续查看其他对象。
阅读有关SO的my previous问题的回复。这听起来是因为提取过程写入的属性与用户通过属性网格可编辑的属性不相交,我不应该同时编辑对象的两个线程有问题。虽然用户可能会看到一个不正确的值,如果它们非常不幸,并且属性网格最终会在非原子写入中读取对象。
但是,我仍然想知道如何设置它以防止用户编辑或查看正在被提取的对象。我对多线程非常陌生,但是我读过的大多数示例都显示了一个单独的令牌对象,用于锁定对实际感兴趣对象的访问。我的另一个previous question的答案证实,通常会创建一个专门用于锁定的单独对象。
所以现在我想知道的是在我的情况下如何处理我有大量的对象?我想创建锁,以防止属性网格显示用户当前正在提取的对象。
我是否需要创建一个单独的锁定对象集合,与我的真实集合保持同步?因此,如果在我的主集合中添加或删除对象,我必须在我的锁集合中添加或删除锁定对象?
我是否锁定实际对象而不是创建单独的令牌锁定对象?
如何将“IsBeingExtracted”布尔属性添加到属性网格可以检查以查看对象是否正在写入的对象中?然后将在提取过程的最开始和结束时设置。
或者某个静态字段如何引用当前被提取到的当前(如果有)对象。然后,属性网格可以检查它要求显示的最新对象是否不是此静态字段引用的对象?如果有多个提取线程,这当然不会起作用。
这样做的最佳/正确方法是什么?就个人而言,我最喜欢布尔属性选项,但想知道其他人真正知道他们在做什么的想法。
答案 0 :(得分:1)
难道你不能仅仅将包含对象的集合设为SynchronizedCollection<T>
吗?它将确保一次只有一个线程可以添加或访问对象。然后,不要担心如何同步对每个对象属性的访问,不要将对象添加到集合中,直到它被填充为止。
这样的事情:
private readonly ICollection<Item> Items = new SynchronizedCollection<Item>();
// Run this on the background thread.
public void PopulateItems()
{
using (var file = File.OpenRead("BigFile.txt"))
using (var reader = new StreamReader(file))
{
while (!reader.EndOfStream)
{
var item = new Item();
PopulateItem(item);
Items.Add(item);
}
}
}
public void PopulateItem(Item item)
{
// Do time-consuming work.
}
属性网格可以愉快地对对象做任何想做的事情,因为当它出现在列表中时,提取线程就完成了。无需显式锁定。
答案 1 :(得分:0)
将对象集合设为字典,然后锁定密钥。
答案 2 :(得分:0)
通常,使用一个或多个锁取决于您希望实现的并发程度。
你拥有的锁越少(例如,一个全局锁),并发性就越少,因为许多操作可以竞争锁并阻止彼此运行。
你拥有的锁越多,你可以实现的并发性就越多,但管理锁的开销也越大。
如果您使用锁保护的内容只是此对象,则使用作为被修改对象的锁是有意义的。您将看到许多使用单独对象的示例,因为您不需要了解保护范围。
在您的具体情况下,我并不真正了解受保护的内容。您是否试图阻止对象属性的并发修改,即后台进程或用户? 由于用户一次不能做多件事,因此不需要大量的并发,因此不需要很多锁。 您可以做的是在属性网格进入编辑模式时以及后台进程即将设置正在编辑的相同属性时使用单个锁(否则,不需要锁)。
答案 3 :(得分:-1)
我认为最好的方法是使用带有超时的ManualResetEvent对象。这样,您可以告诉用户它正在被提取并在几秒钟内重试。
public class Item : IDisposable // I know it is a horrible class name...
{
private readonly ManualResetEvent accessibleEvent = new ManualResetEvent(false);
public void Extract()
{
try
{
// .....
}
finally
{
accessibleEvent.Set(); // unlock
}
}
public void Edit()
{
if (!accessibleEvent.WaitOne(1000)) // wait 1 second
{
// notify user?
}
// ....
}
public void Dispose()
{
((IDisposable)accessibleEvent).Dispose();
}
}