编写高质量代码改善C#程序的157个建议——建议22:确保集合的线程安全

王朝学院·作者佚名  2016-08-28
窄屏简体版  字體: 小  |  中  |  大  |  超大  

建议22:确保集合的线程安全

集合线程安全是指多个线程上添加或删除元素时,线程键必须保持同步。

下面代码模拟了一个线程在迭代过程中,另一个线程对元素进行了删除。

classPRogram

{staticList<Person> list =newList<Person>()

{newPerson() { Name ="Rose", Age =19},newPerson() { Name ="Steve", Age =45},newPerson() { Name ="Jessica", Age =20},

};staticAutoResetEvent autoSet =newAutoResetEvent(false);staticvoidMain(string[] args)

{

Thread t1=newThread(() =>{//确保等待t2开始之后才运行下面的代码autoSet.WaitOne();foreach(variteminlist)

{

Console.WriteLine("t1:"+item.Name);

Thread.Sleep(1000);

}

});

t1.Start();

Thread t2=newThread(() =>{//通知t1可以执行代码autoSet.Set();//沉睡1秒是为了确保删除操作在t1的迭代过程中Thread.Sleep(1000);

list.RemoveAt(2);

});

t2.Start();

}

}classPerson

{publicstringName {get;set; }publicintAge {get;set; }

}

以上代码运行过程会抛出InvalidOperationException:“集合已修改,可能无法执行枚举。”

早在泛型集合出现之前,非泛型集合一般提供一个SyncRoot属性,要保证非泛型集合的线程安全,可以通过锁定该属性来实现。如果上面的集合用ArrayList代替,保证其线程安全则应该在迭代和删除的时候都加上lock,代码如下:

staticArrayList list =newArrayList()

{newPerson() { Name ="Rose", Age =19},newPerson() { Name ="Steve", Age =45},newPerson() { Name ="Jessica", Age =20},

};staticAutoResetEvent autoSet =newAutoResetEvent(false);staticvoidMain(string[] args)

{

Thread t1=newThread(() =>{//确保等待t2开始之后才运行下面的代码autoSet.WaitOne();lock(list.SyncRoot)

{foreach(Person iteminlist)

{

Console.WriteLine("t1:"+item.Name);

Thread.Sleep(1000);

}

}

});

t1.Start();

Thread t2=newThread(() =>{//通知t1可以执行代码autoSet.Set();//沉睡1秒是为了确保删除操作在t1的迭代过程中Thread.Sleep(1000);lock(list.SyncRoot)

{

list.RemoveAt(2);

Console.WriteLine("删除成功");

}

});

t2.Start();

}

以上代码不会抛出异常,因为锁定通过互斥的机制保证了同一时刻只能有一个线程操作集合元素。我们进而发现泛型集合没有这样的属性,必须要自己创建一个锁定对象来完成同步任务。可以通过new一个静态对象来进行锁定,代码如下:

staticList<Person> list =newList<Person>()

{newPerson() { Name ="Rose", Age =19},newPerson() { Name ="Steve", Age =45},newPerson() { Name ="Jessica", Age =20},

};staticAutoResetEvent autoSet =newAutoResetEvent(false);staticobjectsycObj =newobject();staticvoidMain(string[] args)

{//object sycObj = new object();Thread t1 =newThread(() =>{//确保等待t2开始之后才运行下面的代码autoSet.WaitOne();lock(sycObj)

{foreach(Person iteminlist)

{

Console.WriteLine("t1:"+item.Name);

Thread.Sleep(1000);

}

}

});

t1.Start();

Thread t2=newThread(() =>{//通知t1可以执行代码autoSet.Set();//沉睡1秒是为了确保删除操作在t1的迭代过程中Thread.Sleep(1000);lock(sycObj)

{

list.RemoveAt(2);

Console.WriteLine("删除成功");

}

});

t2.Start();

}

转自:《编写高质量代码改善C#程序的157个建议》陆敏技

 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
 
© 2005- 王朝網路 版權所有 導航