2012-10-01 61 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