Multithreading 表中使用EF4和锁定的唯一值
在这段代码中,我有一个EntityFramework4模型,其中有一个“Thing”实体,该实体有一个Id和一个Name(string)列。我想确保当我从多个线程调用FindOrCreateThing(name)时,在Things表中只有一行将使用给定的名称创建 目前,我正在使用锁来完成这项工作,它似乎可以工作。。。但是,还有什么更好的方法呢?在其他项目中如何处理这种常见场景 谢谢Multithreading 表中使用EF4和锁定的唯一值,multithreading,entity-framework,unique,Multithreading,Entity Framework,Unique,在这段代码中,我有一个EntityFramework4模型,其中有一个“Thing”实体,该实体有一个Id和一个Name(string)列。我想确保当我从多个线程调用FindOrCreateThing(name)时,在Things表中只有一行将使用给定的名称创建 目前,我正在使用锁来完成这项工作,它似乎可以工作。。。但是,还有什么更好的方法呢?在其他项目中如何处理这种常见场景 谢谢 class Program { private static string[] names = new s
class Program
{
private static string[] names = new string[] { "Alpha", "Beta", "Delta", "Gamma", "Zeta" };
static void Main(string[] args)
{
// Multiple threads trying to create things, often with the same name,
// but only allow one thread to actually create the record if it doesn't
// exist.
for (int i = 0; i < 100; i++)
{
Thread thread = new Thread(new ThreadStart(MakeThings));
thread.Start();
}
}
static void MakeThings()
{
try
{
foreach (var name in names)
{
Thing t = FindOrCreateThing(name);
Console.WriteLine("Thing record returned: id={0}; name={1}", t.Id, t.Name);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
private static object createLock = new object();
private static Thing FindOrCreateThing(string name)
{
using (EFModel context = new EFModel())
{
// Find the record. If it already exists, return it--we're done.
var thing = (from t in context.Things
where t.Name.Equals(name, StringComparison.CurrentCultureIgnoreCase)
select t).SingleOrDefault();
if (thing == null)
{
// The record does not exist, so wait for lock.
// This will prevent multiple threads from trying to
// create the same record simultaneously.
lock (createLock)
{
// Single thread is here... check if a thread before us
// has already created the record. (I hate having to do this
// same query twice!)
thing = (from t in context.Things
where t.Name.Equals(name, StringComparison.CurrentCultureIgnoreCase)
select t).SingleOrDefault();
if (thing == null)
{
// We're the first thread here, so create the record.
// This should mean that the record is unique in the table.
thing = new Thing { Name = name };
context.Things.AddObject(thing);
context.SaveChanges();
}
}
}
return thing;
}
}
}
类程序
{
私有静态字符串[]名称=新字符串[]{“Alpha”、“Beta”、“Delta”、“Gamma”、“Zeta”};
静态void Main(字符串[]参数)
{
//多个线程试图创建内容,通常使用相同的名称,
//但如果不允许,则只允许一个线程实际创建记录
//存在。
对于(int i=0;i<100;i++)
{
线程线程=新线程(新线程开始(MakeThings));
thread.Start();
}
}
静态void MakeThings()
{
尝试
{
foreach(名称中的变量名称)
{
Thing t=FindOrCreateThing(名称);
WriteLine(“返回的东西记录:id={0};name={1}”,t.id,t.name);
}
}
捕获(例外情况除外)
{
Console.WriteLine(例如ToString());
}
}
私有静态对象createLock=新对象();
私有静态对象FindOrCreateThing(字符串名称)
{
使用(EFModel context=new EFModel())
{
//找到记录。如果它已经存在,返回它——我们完成了。
var thing=(来自context.Things中的t
其中t.Name.Equals(Name,StringComparison.CurrentCultureIgnoreCase)
选择t).SingleOrDefault();
if(thing==null)
{
//记录不存在,请等待锁定。
//这将防止多个线程尝试
//同时创建相同的记录。
锁定(createLock)
{
//单线程在这里…检查前面是否有线程
//已经创造了记录。(我讨厌这样做。)
//相同的查询两次!)
thing=(来自上下文中的t.Things
其中t.Name.Equals(Name,StringComparison.CurrentCultureIgnoreCase)
选择t).SingleOrDefault();
if(thing==null)
{
//我们是这里的第一个线程,所以创建记录。
//这意味着记录在表中是唯一的。
事物=新事物{Name=Name};
context.Things.AddObject(thing);
SaveChanges();
}
}
}
归还物;
}
}
}
只需在DB列上设置一个唯一的约束。然后,您可以摆脱所有锁定,并捕获(不太可能,但可能)异常,如果您搜索、未找到任何内容并创建,而另一个线程则执行相同的操作。如果您发现了这一点,只需重试整个过程。如果实体后面的存储是一些RDBMS,那么在列上创建唯一索引可能会更有效。它将减少到服务器的往返,并且不需要在客户端进行锁定。是的,它是MS SqlServer 2008。很抱歉,我没有指定它。@Eric:创建一个唯一的索引应该可以很好地解决这个问题。或者正如Craig所建议的,一个独特的约束(我认为它在下面做同样的事情……或者我已经读过了)。“重试整个过程”听起来比等待一个锁效率低。线程安全存储过程在这种情况下会更好吗?如果您的应用程序在服务器场上运行,则基于应用程序的锁会固有地被破坏。重试是一个罕见的事件;不要过早地优化只在(相对)不太可能的争用场景中发生的事情。当实际没有引发异常时,try
块的开销接近于零。不,proc不是一个好的解决方案,因为它不能看到事务隔离之外的东西。不要试图重新创造独特的约束;使用它们就行了。好吧,这很有意义。谢谢你的建议。