返回首页


注:本文附带的源代码包含了几个班的Matrix平台。该平台是完全免费的,开源软件。为了获得其源代码和二进制,去网站,请参阅下载部分。简介
,随着多核心处理器的增长,多线程访问数据的问题变得非常重要。本文介绍了一个简单的应用程序同时访问一个通用的。NET数据收集的问题的具体解决方案。尽管正在设计之前释放NET 4.0中的多线程能力的集合,热插拔集合还提供了一个可行的替代System.Collections.Concurrent命名空间提供。使用任何工具,它是最重要的,使用它在适当情况下,所以在这篇文章中,以帮助您与您所选择的使用方案,优点和缺点概述。与在同一时间从多个线程访问一个普通收藏的问题是什么?
从System.Collections命名空间中的默认的泛型集合不是线程安全的,如果在同一时间从多个线程试图访问一个集合实例,这往往会导致"收集修改??异常(系统InvalidOperationException异常)。
下面是一个很简单的例子,问题出现了:

void MethodOne(int x)

{ 

   _collection.Add(x);

}



void MethodTwo()

{

   foreach(int val in _collection)

   {

      // Do something with val.

   }

}  

,如果MethodOne()和MethodTwo()是在同一时间执行两个不同的线程(或更多),这必将导致发生异常。什么热插拔呢?
热交换是一种编程技术,允许建立能够与尽可能多的线程工作,同时根据需要的馆藏没有例外的危险。这些集合设计看起来像正常的泛型集合的很多文章所附的源代码提供的实现提供了列表IList和IDictionary的集合字典,但是同样的机制也可以应用于其他类型的集合。热插拔集合有一个很大的优势呢??锁,少读访问。这将确保绝对最高性能成为可能并发读访问时做的。如何运作?
背后的热交换的原理很简单。它是基于事实,同时操作如修改或清除现有的集合耗时的,需要明确的线程访问控制,但操作的"交换??在单步执行。在我们的例子中,"交换??是指改变​​一个类的实例变量的值从一个实例到另一个。如果我们纪念??代码>挥发??关键字字段,这指示编译器可执行代码,这样"... ...系统总是读取请求一个volatile对象的当前值?? (根据MSDN的)。之间的旧的和新的价值变化的实际操作中始终是线程安全的,因为。NET环境,保证托管指针总是指向一个实际值,如果已分配。
热交换的核心运作原则如下:
当集合的修改要求,热插拔收集准备的内部收集和交换这与现有的实例的新副本全新副本。
所有只读操作都指向目前使用的实例。
这两个简单的步骤就足够,以确保集合是线程安全的,因为所有的访问是对现有的集合静态版本进行的。作为一个这样做的结果 - 一旦一个线程开始遍历集合,它是保证,它将在同一版本的收集工作,它开始时。如果本次迭代过程中发生的变化,这些变化将所有后续迭代尝试访问。
只锁定正在修改集合时发生。这是必需的,以确保所有的改变都反映在最终版本的集合。
在本文中提供的的实现,有也被添加了一些额外的方法,即允许考虑到的事实,收集可能的变化,当一个线程是访问。这些都是TryAdd()和TryGet()方法,往往可以发现类似的方法,在多线程能够集合,与他们一起工作的性质,因为相比传统的集合,是一个有点不同。 实施
热插拔技术的实施是非常简单的。下面是一个局部视图的HotSwapList实现,你可以看到在文章的代码的其余部分。我选择了一个方法,实施两个方面 - 热交换列表操作在两个方向之一是提供内部数据,基于目前的内部参考,和其他修改内部参考??。
这是一个从热交换清单实施的提取,呈现出一些实施的关键功能:{C}热插拔集合VS。NET System.Collections.Concurrent集合
微软NET 4.0中引入的并发线程访问的工作,即设计的集合:BlockingCollection,ConcurrentBag,ConcurrentDictionary,ConcurrentQueue和ConcurrentStack。热插拔集合。NET并发集合的方式同样的问题,并在相当不同的结果,这个结果,当谈到用途及特点有一个很大的区别。 HowSwap集合的优势和缺点,下面是一个列表相比,NET 4.0中的。热插拔的优点最大读取速度
由于其设计,热插拔集合提供了最好的理论上是可行的读取速度。这通常是一个非常小的优势,只有百分之几,但它可以真正做到在非常高的性能应用不同。它也可以证明是有益的的,如果使用大量的线程同时访问(前为6-8或更多),较为常见的场景,将成为在不久的将来引进大量多核CPU。替代操作系统的全面支持
单声道。NET 2.0框架的全力支持,并全力支持NET 4.0仍是给予截止日期(写这篇文章时),所以在情况下,你打算使用并发收集什么比微软的Windows不同,这篇文章是一个出色的解决方案。遍历集合的固定版本
一旦产生一个迭代器,它经过收集,目前存在的迭代器创建。 简单
您只使用您需要什么,你有它的所有源代码,这从长远来看,提高管理和降低风险。热插拔的缺点极低的写/修改速度,特别是大的项目设置
再次由于其设计,修改热插拔集合是一个非常缓慢的过程,因为正在取得一个完整的重复。例如,这可以部分解决,使用的方法,可以让一次添加多个项目??AddRange(中HotSwapList)。有条件的使用
有少数情况下使用列表热插拔收集可以带来错误的结果;这些情况下很容易预见,并详细介绍了"做和不该做什么本文的部分??。应用
热插拔技术是广泛使用的Matrix平台({A2})的组成部分,并已被证明是一个出色的整体解决方案。意识到自己的优势和劣势是非常重要的的,因为他们从一般通用的可用性略有不同。NET集合,并提供了一​​个"专门??的解决方案。
为热插拔使用的一般规则是看为小到中等量的修改集合或集合的情况下,有体积小。技术的使用其他的可能性
热插拔技术还可以在其他情况下使用,例如,在访问类成员实例,没有明确的锁定。下面是一个例子:
void MyMethod()

{

   // Grabbing the instance and storing it in separate local variable prior to using it, 

   // guarantees we shall always use a valid instance, 

   // and there is no danger if other threads change the _instance member 

   // while we are operating.

   CustomClass instance = this._instance;

   If (instance != null)

   {

      instance.PerformAction();

   }

} 

有一个锁定部分的最低数量(使用锁()关键字,监视器类或其他同步项目),保证您的应用程序的最佳性能,最重要的是大大降低了发生死锁的机会。它还简化了代码,并免除程序员经常检查为尽可能多的胎面问题。
使用这种技术,与其他几个人一起,使我们能够建立一个完整的多线程访问策略,极大地简化了多进程线程应用程序的开发,然而,这偏离大大从目前的主题,并应在另一个单独的一篇文章的主题。例子??该做什么和不该做什么HotSwapDictionary
由于其执行的性质,热插拔字典实施的所有操作都在所有可能的情况下,线程安全的。HotSwapList
热插拔列表需要慎用2,特殊情况下使用,它可能会导致错误,如果使用不当发生。
假设我们有以下成员:
HotSwapList<int> _list = new HotSwapList<int>();
案例1:克隆热插拔列表集合正确的
List<int> clone = new List<int>(_list.AsReadOnly()); 

不正确
List<int> clone = new List<int>(_list); 
列表构造函数作为参数的IEnumerable,所以迭代应该是线程安全的,即使我们只传递_list。然而,内部列表构造测试IList接口的类,如果发现使用它。这可能会导致不正确而被修改,迭代集合,并导致异常。案例2:迭代正确的,因为一旦产生一个枚举,它会始终指向集合的初始版本
foreach(int item in _list)

{// Use item.

   int p = item;

}
正确的,因为创建一个只读包装固定的集合中的当前实例
ReadOnlyCollection<int> clone2 = _list.AsReadOnly();

for (int i = 0; i < clone2.Count; i++)

{

    int p = clone2[i];

}
IN正确
for (int i = 0; i < _list.Count; i++)

{

    int p = _list[i];

}
为什么呢?
在最后的情况下,收集可能会改变在运行??代码>为??循环。如果发生这种情况,有没有保证的_List [I]实际上会指向现有成员,这可能会导致异常。这是有没有索引的原因之一。NET 4.0 System.Collections.Concurrent命名空间的能力集合。案例3:暴露在外部访问的始终是安全的正确的
IEnumerable<int> List

{

    get

    {

        return _list;

    }

}
为什么呢?
热插拔收集所有的IEnumerable成员在任何情况下使用是安全的,因为他们依靠统计员使用,而这些点上,以相同的子实例一旦创建。使用
要使用连接到这篇文章的集合的源代码,只需将它们添加到项目,并在您的multh线程环境中使用的类;唯一要牢记的情况下的用法HotSwapList需要做适当的回避可能的例外。

回答

评论会员:iam123 时间:2012/01/26
感谢
{A3}
{A4}
{A5}
{A6}
{A7}
{A8}
{A9}
{A10}
{A11}
{A12}
{A13}
{A14}
{A15}
{A16}
{A17}
{A18}
{A19}
{A20}
{A21}
{A22}
{A23}
{A24}
{A25}
{A26}
{A27}
{A28}
{A29}
{A30}
{A31}
{A32}
{A33}
{A34}
{A35}
{A36}
{A37}
{A38}
{A39}
{A40}
{A41}
{A42}
{A43}
{A44}
{A45}
{A46}
{A47}
{A48}
{A49}
{A50}
{A51}
{A52}
评论会员:尼古拉斯巴特勒 时间:2012/01/26
文章尼斯{S0}

我没有测试,但我怀疑这样一个不可改变的集合效率超过当有几个新的并发集合写道,有绝对没有阅读时的开销。

感谢分享!

尼克
评论会员:大卫Knechtges 时间:2012/01/26
退房SynchronizedCollection - 它是线程
评论会员:!bilo81 时间:2012/01/26
首先,保持良好的工作我想添加更多的MyMethod例如的详细信息。 无效的MyMethod()
{
/ /拼抢的实例,并存储在单独的本地变量在使用它之前,保证我们应始终使用一个有效的实例,
/ /有没有危险,如果其他线程改变_instance成员,而我们是经营。
CustomClass实例= this._instance;
(例如!= NULL)
{
instance.PerformAction()
&# 160;}
}

在你的榜样,一切都很好,如果this.instance例如设置为null,或设置为从另一个线程的一个新实例,因为你仍然可以使用本地的实例,这是一个旧的实例的引用,但如果需要被syncronized的performAction或另一个线程改变this._instance不喜欢this.instance =新CustomClass()或this._instance = NULL,但像this._instance.ModifyStateOfTheClassWhichNeedToBeSynchronize(),那么你没有获得这样的线程安全的(除非CustomClass是不可改变的或行为如一个不可改变的类)。
我建议你​​强调和突出,我知道你知道,你实际上只是说有没有危险,如果其他线程改变_instance成员,而我们是经营,这是正确的,但有人可能认为这有与线程的_instance本身seafety ...

有5

无论如何,非常好,
评论会员:驰援Timnev 时间:2012/01/26
感谢您的输入。你的说法的确是正确的 - 实例本身的内部数据是不是线程安全的,当然这并不意味着要执行修改。
我相信里面的方法的评论揭示一些关于该主题:"(IT)保证我们应始终使用一个有效的实例"和"有没有危险,如果其他线程改变_instance成员"大约只有获得一个有效的实例不说话其内部
评论会员:bilo81 时间:2012/01/26
!感谢您的回答,再次做好
评论会员:supercat9 时间:2012/01/26
如果一个集合之间的"洋"读取,克隆集合书面多次每次写入后会是浪费时间。它可能会有所帮助,以便收到多个更新的集合的可写副本之前的只读版本复制。{​​BR}
是这样的:

  ... before updating, within a lock ...

  If updateableVersion is Nothing Then

    updateableVersion = theList.Clone

  EndIf

  ...

 

  ... After updates are complete, within a lock ...

  readonlyVersion = updateableVersion

  updateableVersion = Nothing

  ...


你的语言是有点含糊这里:使用实际的数据收集的两个实例,并尽快完成更新的交换与更新现有的热插拔操作。您可以保留两个对象的引用,但句子听起来像你重复使用相同的两个对象实例。在现实中,一旦你作出了您的实例作为唯一的,它必须永远一成不变的,是不能被重用。
可参考
另一种设计,这将改善性能的复杂度为代价的字典将有一个数组的字典,一个键的哈希选择。添加或删除一个关键,将需要克隆的阵列,并克隆要修改的字典,但不会要求任何克隆其他字典。这种方法的最大限制将调整数组的大小会需要重建的一切(旧数组的只读副本仍然会继续工作,虽然)
评论会员:驰援Timnev 时间:2012/01/26
没有怀疑的余地存在针对特定的情况下优化。优化的情况下你的轮廓的确会在某些情况下产生的性能增益,但是它会显著复杂的解决方案的设计和实施,最重要的是,它超越了这一解决方案的范围。同样也适用于延长它的复杂性,优化字典

热插拔技术的核心,是执行"即时"交换每一个变化是时间 - 简单和直截了当。如果一个人最终的一般使用线程安全的集合,有更好的选择肯定比这里提出的简约
评论会员:。supercat9 时间:2012/01/26
我是可以理解的"另类办法",可能是过度的文章点。不过,我会建议措词的句子,我引用了提高清晰度(例如"热插拔操作,有时与旧的价值观和正在编辑一个新的两个实例实际数据收集 - 一个不可改变之一 - 和更换后者与前尽快完成更新。")作出明确的代码并不简单的两个实例之间切​​换来回。

此外,我会建议,提供执行多个更新,而不需要重新克隆的手段,不仅让以提高性能,但也将提​​供一个"交易"的设施,所以,如果如,是必要的,同时删除一个键,并添加另一个,这样的操作会被视为一个单一的步骤