2012-10-01 154 views
4

我想在我的Windows应用程序在c#中创建我自己的事件系统。要做到这一点,我写了下面的类:
静态类的线程安全

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进行类是线程安全的。因为我将在我的应用程序中同时创建数百个线程。所有的线程很可能会使用这个类来生成事件。并且当从不同线程调用AddEvent函数时,函数可以从类外调用。

简单地说,你能告诉我这个设计适用于多线程的Windows应用吗?如果这不是线程安全的,那我该如何让我的类或其成员线程安全?我应该使用同步对象来锁定整个EventManager类,还是应该使用readwritelocker锁定我的s_listEvents静态成员?

+5

数百个线程并不理想。即使阻塞,线程也相对昂贵。 –

+0

我的应用程序必须通过tcp从远程计算机收集数据。这个收集操作必须在给定的时间内完成。所以我必须创建许多线程。我可以通过硬件负载平衡来减少线程数量(就像使用多个服务器一样),但在任何情况下,我的应用程序都是多线程应用程序。 – Fer

+0

为什么你需要创建自己的事件系统? –

回答

2

而不是使用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; 
     } 

    } 
} 

编辑使线程安全:

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

如果不是,则使用lock,其总体上具有更好的性能。

见链接:

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

+0

谢谢,但ConcurrentBag似乎不能在.net framework 3.5上工作。因为我使用的是框架3.5,所以我不能使用ConcurrentBag实现。或者我应该自己实施这个逻辑。 – Fer

+0

@Dmitry哪一个是理想的,ReaderWriterLock或锁定_syncObject对象? – Fer

+1

@Fer:ReaderWriterLock在'lock'上没有太多的性能,使用'lock'会更简单,更具可读性 –

2

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

阅读器/作家锁似乎是一个很好的选择。

+0

我不确定ReaderWriter锁是否是这里的最佳选择。作家速度非常快,读者速度要慢很多。它将需要测试。 –

+0

我认为会有更多的读者比作家... – Patrik

1

您可以使用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(); 
     } 
    } 
} 
0

下面的链接将是有益的:

How to make a class Thread Safe

private object _lock; 

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

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

即使您访问的服务器数量可能不会超过100多个,但您可能想要使用线程池,详情请参阅http://msdn.microsoft.com/en-us/library/0ka9477y(v=vs.90).aspx。这将给你一个线程池,用于简单的“检入 - 下载数据 - 检出”类似于你正在描述的那个任务。

在编写多线程应用程序时,重要的是要考虑底层存储的使用模式。如果您的应用程序每秒会执行数百次添加,您可能需要考虑拥有底层数据结构的只读副本,并且每次尝试按日期获取记录时都不会阻塞整个系统。有关详细介绍,请参见Intel's Optimization Guide

+0

考虑到数据结构的只读副本看起来不错,性能方面。我会阅读并思考你的建议。谢谢。 – Fer