这段代码实际上是线程安全的吗?

时间:2015-07-12 16:57:42

标签: c# multithreading

我对在Microsoft的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);

我认为应该用锁来保护它。

2 个答案:

答案 0 :(得分:2)

  

这段代码实际上是线程安全的吗?

不,不是。

根据documentationArrayList实例不支持并发修改,除非它由Synchronized方法返回,并且在这里不是这样。

虽然可能并不明显,但在您的示例中可能会发生并发修改。 Task排队到ThreadPool,它将由此池中的某个线程运行。如果用户双击btnCreateStudent将创建两个任务,并且由于Thread.Sleep不是非常精确,无论如何,不​​必立即执行任务(例如,ThreadPool队列可能已满)因此,虽然两个任务在不同时间安排,但可以同时执行。

  

使此代码线程安全的最佳方法是什么?

这取决于你所说的“最好的”。

第一个解决方案是使用Synchronized方法创建ArrayList

private ArrayList students = ArrayList.Synchronized(new ArrayList());

但你仍然需要使用锁来枚举这个列表。

  

通过集合枚举本质上不是线程安全的   程序。即使集合是同步的,其他线程也可以   仍然修改集合,这会导致枚举器抛出一个   例外。为了在枚举期间保证线程安全,您可以   要么在整个枚举过程中锁定集合,要么抓住它   由其他线程所做的更改导致的异常。

另一种解决方案是使用List<T>并在访问集合的任何地方添加锁。 List<T>优于ArrayList,因为它包含元素类型,因此您不必在读取时强制转换它们,或者您不会意外地将不兼容的类型添加到集合中。

如果您不关心项目的顺序,那么您应该使用ConcurrentBag<T>,这不需要任何锁定。

答案 1 :(得分:1)

线程安全性”在很大程度上取决于您在单独线程之外执行的操作。 如果在任务运行期间没有完成任务之外的students,则代码是线程安全的。

如果您在任务的生命周期内使用students,则应同步访问权限。 您可以使用lockother synchronization methods

执行此操作

您当然也可以使用concurrent collections.

中的一些内容