Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/entity-framework/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Multithreading 表中使用EF4和锁定的唯一值_Multithreading_Entity Framework_Unique - Fatal编程技术网

Multithreading 表中使用EF4和锁定的唯一值

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

在这段代码中,我有一个EntityFramework4模型,其中有一个“Thing”实体,该实体有一个Id和一个Name(string)列。我想确保当我从多个线程调用FindOrCreateThing(name)时,在Things表中只有一行将使用给定的名称创建

目前,我正在使用锁来完成这项工作,它似乎可以工作。。。但是,还有什么更好的方法呢?在其他项目中如何处理这种常见场景

谢谢

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不是一个好的解决方案,因为它不能看到事务隔离之外的东西。不要试图重新创造独特的约束;使用它们就行了。好吧,这很有意义。谢谢你的建议。