4

我想用 c# 在我的 windows 应用程序中创建自己的事件系统。为此,我编写了以下类:

internal class EventManager
{
    private static List<EventRecord> s_listEvents = new List<EventRecord>();

    public static void AddEvent(EventRecord record)
    {
        record.EventDate = DateTime.Now;
        s_listEvents.Add(record);
    }

    public static List<EventRecord> GetRecordsByDate(DateTime date)
    {
        var r = (from l in s_listEvents
                 where l.EventDate >= date
                 select l).ToList<EventRecord>();
        return r;
    }
}

我想确保 EventManager 类是线程安全的。因为我要在我的应用程序中同时创建数百个线程。所有线程很可能会使用这个类来生成事件。并且在从不同线程调用函数GetRecordsByDate时,可能会从类外部调用函数。 简单地说,你能告诉我这种设计适合多线程windows应用程序吗?如果这不是线程安全的,那么我怎样才能使我的类或其成员线程安全?我应该使用同步对象来锁定整个类还是应该使用 readwritelocker 来锁定我的静态成员?AddEvent

EventManagers_listEvents

4

5 回答 5

3

而不是使用List<T>,你应该使用ConcurrentBag<T>

ConcurrentBag 是一个线程安全的包实现,针对同一线程将同时生产和使用包中存储的数据的场景进行了优化。

更多信息:

http://msdn.microsoft.com/en-us/library/dd381779.aspx

此外,请注意创建要访问的线程数,超过 100 个线程降低性能,因为切换上下文需要时间。

编辑:对于 .NET 3.5,您可以使用简单的方法使线程安全lock

internal class EventManager
{
    private static List<EventRecord> s_listEvents = new List<EventRecord>();
    private static object _syncObject = new object();


    public static void AddEvent(EventRecord record)
    {
        record.EventDate = DateTime.Now;
        lock(_syncObject)
        {
           s_listEvents.Add(record); 
        }

    }

    public static List<EventRecord> GetRecordsByDate(DateTime date)
    {
        lock (_syncObject)
        {
             var r = (from l in s_listEvents
                 where l.EventDate >= date
                 select l).ToList<EventRecord>();

             return r;
        }

    }
}

编辑

根据您的情况,如果您非常频繁地读取数据,那么对于整个应用程序使用ReaderWriterLockSlimwithReaderWriterLock会更好,因为它允许多个线程读取数据。

如果没有,请使用lock通常具有更好性能的。

见链接:

http://blogs.msdn.com/b/pedram/archive/2007/10/07/a-performance-comparison-of-readerwriterlockslim-with-readerwriterlock.aspx

于 2012-10-01T09:05:15.363 回答
2

由于该类是静态的,因此您应该锁定 s_listEvents 成员。调用者很有可能无法访问共享锁对象,除非您将锁作为 EventManager 本身(或任何其他静态类)上的静态成员提供。如果是这种情况,您不妨s_listEvents直接在 EventManager 中实现访问锁定。这样可以避免调用者忘记获取锁的问题。

读/写锁似乎是一个不错的选择。

于 2012-10-01T09:04:49.733 回答
2

您可以使用ReaderWriterLock类:

internal class EventManager
{
    static ReaderWriterLock rwl = new ReaderWriterLock();

    private static List<EventRecord> s_listEvents = new List<EventRecord>();

    public static void AddEvent(EventRecord record)
    {
        record.EventDate = DateTime.Now;
        rwl.AcquireWriterLock(0);
        try
        {
            s_listEvents.Add(record);
        }
        finally
        {
            rwl.ReleaseWriterLock();
        }
    }

    public static List<EventRecord> GetRecordsByDate(DateTime date)
    {
        rwl.AcquireReaderLock(0);
        try
        {
            var r = (from l in s_listEvents
                     where l.EventDate >= date
                     select l).ToList<EventRecord>();
            return r;
        }
        finally
        {
            rwl.ReleaseReaderLock();
        }
    }
}
于 2012-10-01T09:14:22.230 回答
1

您的问题的最基本答案如下:为了使您的解决方案线程安全,您必须保护您的数据存储不被同时访问。这是通过在访问列表的任何时候锁定您的列表来完成的。这意味着当您对列表进行迭代、添加或删除时,您必须锁定该区域。

即使您正在访问该数量的服务器,您可能也不想生成 100 多个线程,而是您可能想要使用线程池,请参阅http://msdn.microsoft.com/en-us/library/0ka9477y(v =vs.90).aspx了解详情。这将为您提供一个线程池,用于简单的“签入 - 下载数据 - 签出”任务,例如您正在描述的任务。

在编写多线程应用程序时,考虑底层存储的使用模式很重要。如果您的应用程序每秒将执行数百次添加,您可能需要考虑拥有一个底层数据结构的只读副本,这样您就不会在每次尝试按日期获取记录时阻塞整个系统。详细介绍见英特尔优化指南

于 2012-10-01T09:30:08.917 回答
1

以下链接将很有用:

如何使类线程安全

private object _lock;

public static void AddEvent(EventRecord record)
{
    lock (_lock)
    {
        record.EventDate = DateTime.Now;
        s_listEvents.Add(record);
    }
}
于 2012-10-01T09:14:46.457 回答