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

C# 这段代码实际上是线程安全的吗?,c#,multithreading,C#,Multithreading,关于我在微软的C#教程网页上找到的以下代码段,我有一个问题。在代码中,它们提供了任务的演示。在事件处理程序中,它们创建一个更新未受保护集合的任务 这段代码是线程安全的吗?在我看来不是。使此代码线程安全的最佳方法是什么 private ArrayList students = new ArrayList(); private void btnCreateStudent_Click(object sender, RoutedEventArgs e) { Student newStudent

关于我在微软的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);
我认为应该用锁来保护它。

“线程安全”在很大程度上取决于您在单独线程之外所做的事情。 如果在任务运行期间,在任务外部不接触学生,则代码是线程安全的

如果在任务的生命周期内在外部使用
学生
,则应同步访问。 您可以使用

当然,您也可以使用一些

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

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).");
}
不,不是

根据定义,
ArrayList
实例不支持并发修改,除非它是通过
Synchronized
方法返回的,而这里不是这种情况

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

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

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).");
}
这取决于你所说的“最好”是什么意思

第一种解决方案是使用该方法创建
ArrayList

但您仍然必须使用锁来枚举此列表

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

另一种解决方案是在访问集合的任何位置使用和添加锁。
List
优于
ArrayList
,因为它包含元素类型,所以不必在读取时强制转换它们,也不会意外地将不兼容的类型添加到集合中


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

您的意思是线程安全?您是在哪个MSDN页面上这样做的?您特别想知道什么是线程安全的?添加呼叫?实际上,我不确定这段代码是否能正常工作,因为您正在任务中调用MessageBox.Show。这些应该在UI线程上运行。我发现了一个更为复杂的代码,不是MSDNDon't use
ArrayList
,它是泛型退出前1.1天从.NET遗留下来的。您应该改为使用
List
。MessageBox.Show()是一个线程安全调用(因为它是静态的)。ArrayList.Add()不是。我定期调用该任务。每次鼠标单击将创建一个单独的踏板。什么会导致许多竞争对手threads@DavidYachnis然后你需要一个同步。