C# 如何控制多个线程对对象集合的访问?

C# 如何控制多个线程对对象集合的访问?,c#,winforms,multithreading,locking,propertygrid,C#,Winforms,Multithreading,Locking,Propertygrid,我正在编写一个应用程序,它显示一个对象列表,用户可以选择这些对象,然后通过PropertyGrid控件查看和编辑其属性。通过使用辅助线程从文件中提取信息的耗时过程填充对象的属性。但我也希望允许用户在提取过程中继续查看其他对象 在阅读了对问题的回答之后。这听起来像是因为提取过程编写的属性与用户通过属性网格可编辑的属性不相交,所以两个线程同时编辑对象不会有问题。虽然用户有可能看到一个不正确的值,如果它们是非常不吉利的,并且属性网格最终在非原子写的中间读取对象。 但是,我仍然想知道如何设置这个来防止用

我正在编写一个应用程序,它显示一个对象列表,用户可以选择这些对象,然后通过PropertyGrid控件查看和编辑其属性。通过使用辅助线程从文件中提取信息的耗时过程填充对象的属性。但我也希望允许用户在提取过程中继续查看其他对象

在阅读了对问题的回答之后。这听起来像是因为提取过程编写的属性与用户通过属性网格可编辑的属性不相交,所以两个线程同时编辑对象不会有问题。虽然用户有可能看到一个不正确的值,如果它们是非常不吉利的,并且属性网格最终在非原子写的中间读取对象。

但是,我仍然想知道如何设置这个来防止用户编辑或查看正在提取的对象。我对多线程非常陌生,但我读过的大多数示例都显示了创建一个单独的令牌对象,用于锁定对实际感兴趣对象的访问。对我的另一个问题的回答证实了创建这样一个单独的对象是典型的,专门用于锁定

现在我想知道的是,在我拥有大量对象的情况下,这是如何处理的?我想创建锁,防止属性网格显示用户选择的对象(如果该对象当前正在被提取到)

我是否需要创建一个单独的锁对象集合,并与我的真实集合保持同步?因此,如果从我的主集合中添加或删除对象,我必须从我的锁集合中添加或删除锁对象

我是否锁定到实际对象,而不是创建单独的令牌锁定对象

如何将一个ISBEIN提取的布尔属性添加到属性网格可以检查的对象中,查看该对象是否处于被写入的中间位置?这将在提取过程的最开始和最末尾设置

或者在某个地方有一个静态字段,它引用当前对象(如果提取到当前对象)。然后,属性网格可以检查请求显示的最新对象是否不是此静态字段引用的对象?如果有多个提取线程,这当然不起作用


这样做的最佳/正确方法是什么?我个人最喜欢布尔属性选项,但是我想知道其他真正知道自己在做什么的人是怎么想的。

将对象集合设置为字典,然后锁定键。

将对象集合设置为字典,然后锁定键。

我认为最好的方法是使用带有超时的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();
    }
}

我认为最好的方法是使用带有超时的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();
    }
}

通常,使用一个或多个锁取决于您想要实现的并发程度

你拥有的锁越少,比如说,一个全局锁,并发性就越低,因为许多操作可以竞争锁并阻止彼此运行

您拥有的锁越多,可以实现的并发性就越多,但是管理锁的开销就越大

如果您用锁保护的只是这个对象,那么使用被修改对象的锁是有意义的。您将看到许多使用单独对象的示例,因为这样您就不需要考虑保护的范围

就你的具体情况而言,我真的不明白什么是被保护的。您是否试图阻止并发修改对象的属性,即后台进程或用户? 由于用户一次不能做多件事,因此不需要很多并发性,因此不需要有很多锁。
您可以做的是在属性网格进入编辑模式时使用一个锁,当后台进程将要设置正在编辑的相同属性时,不需要锁。

通常,使用一个或多个锁取决于您想要实现的并发程度

你拥有的锁越少,比如说,一个全局锁,并发性就越低,因为许多操作可以竞争锁并阻止彼此运行

您拥有的锁越多,可以实现的并发性就越多,但是管理锁的开销就越大

如果要保护的对象是被修改的对象,则使用该对象的锁是有意义的 锁只是这个对象。您将看到许多使用单独对象的示例,因为这样您就不需要考虑保护的范围

就你的具体情况而言,我真的不明白什么是被保护的。您是否试图阻止并发修改对象的属性,即后台进程或用户? 由于用户一次不能做多件事,因此不需要很多并发性,因此不需要有很多锁。
您可以做的是在属性网格进入编辑模式时,以及后台进程即将设置正在编辑的相同属性时,使用单个锁。否则,不需要锁。

难道您不能将保存对象的集合设置为SynchronizedCollection吗?它将确保一次只有一个线程可以添加或访问对象。然后,不要担心如何同步访问每个对象的属性,在填充对象之前不要将其添加到集合中

大概是这样的:

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.
}

属性网格可以愉快地对对象执行任何它想要的操作,因为当它出现在列表中时,提取线程已经完成了对它的处理。不需要显式锁定。

您不能将保存对象的集合设置为SynchronizedCollection吗?它将确保一次只有一个线程可以添加或访问对象。然后,不要担心如何同步访问每个对象的属性,在填充对象之前不要将其添加到集合中

大概是这样的:

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.
}

属性网格可以愉快地对对象执行任何它想要的操作,因为当它出现在列表中时,提取线程已经完成了对它的处理。不需要显式锁定。

为什么必须保持锁定一秒钟或更长时间?同步线程的一部分是最小化它们可能处于争用状态的时间长度。@Rory-这是等待编辑项目的时间量,而不是保持锁的时间量。为什么锁必须保持一秒或更长时间?同步线程的一部分是最小化它们可能处于争用状态的时间长度。@Rory-这是等待编辑项的时间量,而不是保持锁的时间量。