这段代码实际上是线程安全的吗?
本文关键字:安全 线程 实际上 段代码 代码 | 更新日期: 2023-09-27 18:02:15
我有一个关于以下代码段的问题,我在微软的c#教程网页上找到了。在代码中,他们提供了一个任务演示。在事件处理程序中,它们创建一个任务来更新未受保护的集合。
这个代码是线程安全的吗?在我看来并非如此。使此代码线程安全的最佳方法是什么?
private ArrayList students = new ArrayList();
private void btnCreateStudent_Click(object sender, RoutedEventArgs e)
{
Student newStudent = new Student();
newStudent.FirstName = txtFirstName.Text;
newStudent.LastName = txtLastName.Text;
newStudent.City = txtCity.Text;
ClearForm();
Task task1 = new Task(() => AddToCollection(newStudent));
task1.Start();
ClearForm();
}
private void AddToCollection(Student student)
{
Thread.Sleep(5000);
students.Add(student);
int count = students.Count;
MessageBox.Show("Student created successfully. Collection contains " + count.ToString() + " Student(s).");
}
我不同意下面的说法
students.Add(student);
这段代码实际上是线程安全的吗?
不,它不是。
根据文档,ArrayList
实例不支持并发修改,除非它由Synchronized
方法返回,但这里不是这样。
虽然它可能不明显,但并发修改可能发生在您的示例中。Task
排在ThreadPool
后面,它将由这个池中的一些线程运行。如果用户双击btnCreateStudent
,将创建两个任务,并且由于Thread.Sleep
不是非常精确,并且无论如何,任务不必立即执行(例如ThreadPool队列可能已满),因此两个任务虽然在不同的时间调度,但可能同时执行。
使代码线程安全的最好方法是什么?
这取决于你对"最好的"的定义。
第一个解决方案是使用Synchronized
方法创建ArrayList
。
private ArrayList students = ArrayList.Synchronized(new ArrayList());
但是您仍然需要使用锁来枚举这个列表。
枚举一个集合本质上不是线程安全的过程。即使一个集合是同步的,其他线程也可以仍然修改集合,这将导致枚举数抛出例外。为了保证枚举期间的线程安全,可以在整个枚举过程中锁定集合,或者捕获由其他线程所做的更改导致的异常。
另一种解决方案是使用List<T>
并在访问集合的任何地方添加锁。List<T>
优于ArrayList
,因为它包含元素类型,所以您不必在读取时强制转换它们,或者您不会意外地将不兼容的类型添加到集合中。
如果你不关心项目的顺序,那么你应该使用ConcurrentBag<T>
,它不需要任何锁。
"线程安全性"在很大程度上取决于您在单独线程之外所做的工作。如果在任务运行期间,在任务之外根本不触及students
,则代码是线程安全的。
如果您在任务的生存期内在外部使用students
,您应该同步访问。您可以使用lock
或其他同步方法来完成此操作。
你当然也可以使用一些并发集合