多线程,lambda表达式和局部变量
我的问题是,在下面的代码中,我可以确定实例方法将访问我认为他们会的变量,还是可以在我还在工作时由另一个线程更改? 闭包与此有什么关系,也就是说我会在IEnumerable<T>
的本地副本上工作,因此枚举是安全的吗?
为了解释我的问题,如果我从不写入共享变量,是否需要锁定?
public class CustomerClass
{
private Config cfg = (Config)ConfigurationManager.GetSection("Customer");
public void Run()
{
var serviceGroups = this.cfg.ServiceDeskGroups.Select(n => n.Group).ToList();
var groupedData = DataReader.GetSourceData().AsEnumerable().GroupBy(n => n.Field<int>("ID"));
Parallel.ForEach<IGrouping<int, DataRow>, CustomerDataContext>(
groupedData,
() => new CustomerDataContext(),
(g, _, ctx) =>
{
var inter = this.FindOrCreateInteraction(ctx, g.Key);
inter.ID = g.Key;
inter.Title = g.First().Field<string>("Title");
this.CalculateSomeProperty(ref inter, serviceGroups);
return ctx;
},
ctx => ctx.SubmitAllChanges());
}
private Interaction FindOrCreateInteraction(CustomerDataContext ctx, int ID)
{
var inter = ctx.Interactions.Where(n => n.Id = ID).SingleOrDefault();
if (inter == null)
{
inter = new Interaction();
ctx.InsertOnSubmit(inter);
}
return inter;
}
private void CalculateSomeProperty(ref Interaction inter, IEnumerable<string> serviceDeskGroups)
{
// Reads from the List<T> class instance variable. Changes the state of the ref'd object.
if (serviceGroups.Contains(inter.Group))
{
inter.Ours = true;
}
}
}
我似乎找到了答案,在这个过程中,也是这个问题。
真正的问题是本地“变量”是否是可信的并发访问。 答案是否定的,如果他们碰巧没有以线程安全的方式处理内部状态,则所有投注都关闭。 关闭不起作用,它只是捕获对所述对象的引用。
在我的具体情况中 - 从IEnumerable<T>
并发读取并且没有写入它,它实际上是线程安全的,因为每次调用foreach
, Contains()
, Where()
等都会得到一个全新的IEnumerator
,它只是可见的来自请求它的线程。 但是,任何其他对象也必须逐个检查。
所以,万岁,没有锁或同步集合:)
感谢@ebb和@Dave,虽然你没有直接回答这个问题,但你指出了我的正确方向。
如果您对结果感兴趣,那么可以使用Thread.SpinWait
在我的家用PC(四核)上运行,以模拟一行的处理时间。 在本地网络上使用SQL Server的双核超线程机器上,真实应用程序的性能提高了近2倍(01:03 vs 00:34)。
单线程,使用foreach
。 我不知道为什么,但是跨核心上下文切换的数量非常多。
使用Parallel.ForEach
,在需要的地方使用线程本地锁定。
现在,从我所知道的,你的实例方法没有使用任何成员变量。 这使他们无国籍,因此线程安全。 但是,在这种情况下,为了代码清晰度和轻微的性能优势,最好将它们标记为“静态”。
如果这些实例方法使用成员变量,那么它们只会与该变量一样是线程安全的(例如,如果您使用了简单列表,它不会是线程安全的,您可能会看到奇怪的行为)。 长话短说,成员变量是易线程安全的敌人。
这是我的重构(免责声明,未经测试)。 如果你想提供传入的数据,如果你将它们作为参数传递并且不把它们保存为成员变量,你会保持安全:
更新:你要求一种方法来引用你的只读列表,所以我已经添加了并删除了静态标签(以便实例变量可以共享)。
public class CustomerClass
{
private List<string> someReadOnlyList;
public CustomerClass(){
List<string> tempList = new List<string>() { "string1", "string2" };
someReadOnlyList = ArrayList.Synchronized(tempList);
}
public void Run()
{
var groupedData = DataReader.GetSourceData().AsEnumerable().GroupBy(n => n.Field<int>("ID"));
Parallel.ForEach<IGrouping<int, DataRow>, CustomerDataContext>(
groupedData,
() => new CustomerDataContext(),
(g, _, ctx) =>
{
var inter = FindOrCreateInteraction(ctx, g.Key);
inter.ID = g.Key;
inter.Title = g.First().Field<string>("Title");
CalculateSomeProperty(ref inter);
return ctx;
},
ctx => ctx.SubmitAllChanges());
}
private Interaction FindOrCreateInteraction(CustomerDataContext ctx, int ID)
{
var query = ctx.Interactions.Where(n => n.Id = ID);
if (query.Any())
{
return query.Single();
}
else
{
var inter = new Interaction();
ctx.InsertOnSubmit(inter);
return inter;
}
}
private void CalculateSomeProperty(ref Interaction inter)
{
Console.Writeline(someReadOnlyList[0]);
//do some other stuff
}
}
链接地址: http://www.djcxy.com/p/56629.html