【发布时间】:2009-09-23 20:23:01
【问题描述】:
我有可能在两个不同线程上同时运行的这两段代码:
users = (from user in users
orderby user.IsLoggedIn descending ,
user.Username
select user).ToList();
和:
users=null;
第二段代码将在应用程序的主 UI 线程上运行。如何防止在 LINQ 操作完成之前将用户设置为 null?将用户集合封装在一个属性中,并在 getter 和 setter 上加锁,我认为这还不够……
编辑: 我构建了以下测试类:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
namespace MultiThreading_Spike
{
class Program
{
private static List<User> users;
private static Timer timer;
static void Main(string[] args)
{
timer=new Timer(OrderUsers,null,5,5);
for (int i = 0; i < 10000; i++)
{
ResetUsers();
Thread.Sleep(5);
users = new List<User>
{
new User {UserName = "John"},
new User {UserName = "Peter"},
new User {UserName = "Vince"},
new User {UserName = "Mike"}
};
Thread.Sleep(5);
}
ResetUsers();
Thread.Sleep(5)
Debug.Assert(users==null);
}
private static void OrderUsers(object state)
{
if(users==null)return;
Thread.Sleep(2);
try
{
users = (from user in users
orderby user.IsLoggedIn descending ,
user.UserName
select user).ToList();
}
catch(Exception e)
{
Console.WriteLine("Error: {0}",e.Message);
}
}
private static void ResetUsers()
{
users = null;
}
}
public class User
{
bool isLoggedIn;
public bool IsLoggedIn
{
get { return isLoggedIn; }
set { isLoggedIn = value; }
}
private string userName;
public string UserName
{
get { return userName; }
set { userName = value; }
}
}
}
此代码失败,OrderUsers 方法中出现空引用异常。 然后我实施了建议的解决方案: 解决方案一:
//identical code omitted
private static void OrderUsers(object state)
{
lock(syncRoot)
{
if(users==null)return;
Thread.Sleep(2);
try
{
users = (from user in users
orderby user.IsLoggedIn descending ,
user.UserName
select user).ToList();
}
catch(Exception e)
{
Console.WriteLine("Error: {0}",e.Message);
}
}
}
private static void ResetUsers()
{
lock(syncRoot)
{
users = null;
}
}
}
没有例外!
解决方案 2:
private static void OrderUsers(object state)
{
if(users==null)return;
var tempUsers = users;
Thread.Sleep(2);
try
{
tempUsers = (from user in tempUsers
orderby user.IsLoggedIn descending ,
user.UserName
select user).ToList();
}
catch(Exception e)
{
Console.WriteLine("Error: {0}",e.Message);
}
users = tempUsers;
}
没有空引用异常,但最终用户为空的Assert可能会失败。
解决方案 3:
private static void OrderUsers(object state)
{
if(users==null)return;
try
{
users.Sort((a, b) => Math.Sign(-2 * a.IsLoggedIn.CompareTo(b.IsLoggedIn) + a.UserName.CompareTo(b.UserName)));
}
catch(Exception e)
{
Console.WriteLine("Error: {0}",e.Message);
}
}
没有例外。我一直有一种烦人的感觉,即排序可能“就位”,但不一定是原子的。
解决方案 4: 我无法编译它。 VolatileRead 方法有一个重载对象,但我无法让它接受一个列表
【问题讨论】:
标签: c# .net multithreading synchronization