我有大约同一时间开始的多个线程---所有线程都执行相同的代码路径。每个线程都需要将记录写入数据库中的表。如果该表不存在,则应创建该表。显然,两个或多个线程可能会将表视为缺失,并尝试创建它。
确保此特定代码块仅由一个线程执行一次的首选方法是什么。
当我在.NET 2.0上使用C#编写时,我认为该方法将是框架/语言中立的。
答案 0 :(得分:2)
这样的事情应该有用......
private object lockObject = new object();
private void CreateTableIfNotPresent()
{
lock(lockObject)
{
// check for table presence and create it if necessary,
// all inside this block
}
}
让你的线程调用CreateTableIfNotPresent
函数。 lock
块将确保没有线程能够同时执行块内的代码,因此没有线程能够在另一个创建它时查看该表不存在。
答案 1 :(得分:2)
互斥锁可确保一次只能由一个线程运行一段特定的代码(或几段代码)。您可以聪明并为每个表使用不同的互斥锁,或者只是将整个初始化块一次约束到一个线程。
信号量(或一组信号量)可以执行完全相同的功能。
大多数锁实现将在内部使用互斥锁,因此请查看您正在使用的语言或库中已有的锁代码。
@ebpower是正确的,在某些应用程序中,实际上更有效地捕获由于尝试多次创建同一个表而导致的异常,尽管在您的示例中可能不是这种情况。
然而,还有许多其他的处理方式。例如,您可以使用单线程ExecutorService(抱歉,我只能找到Java引用),它负责创建工作线程发现的任何表缺失。如果它对同一个表有两个请求,它就会忽略后面的那些。
Memoizer上的变体(记住表格引用,必要时首先创建它们)在这种情况下也可以使用。本书Java Concurrency In Practice介绍了一个很好的Memoizer class的实现,但是使用有效的并发构建块移植到任何其他语言都非常简单。
答案 2 :(得分:1)
这是Semaphores的用途。
答案 3 :(得分:0)
您可能甚至不需要打扰锁,因为您的数据库不应该让您创建具有相同名称的多个表。为什么不捕获适当的异常,如果两个线程试图创建相同的表,一个赢得并继续,而另一个恢复并继续。
答案 4 :(得分:0)
您可能想尝试使用静态构造函数来获取表的引用。
根据MSDN (.net 2.0),静态构造函数用于初始化任何静态数据,用于执行仅需执行一次的特定操作。 < / p>
此外,CLR会自动保证静态构造函数每个AppDomain只执行一次 并且线程安全。
有关详细信息,请通过Jeffrey Richter的C#查看CLR的第8章。
答案 5 :(得分:0)
我使用线程同步对象,例如ManualResetEvent虽然听起来像你愿意竞争条件,这可能意味着你有设计问题
有些帖子建议使用互斥体 - 除非你的线程在不同的进程上运行,否则这是一种过度杀伤力
其他人建议使用锁 - 这很好,但锁定可能导致对数据的过度悲观锁定,这可能会抵消首先使用线程的好处。
一个更基本的问题是你为什么这样做呢?线程为问题域带来了哪些好处?并发是否解决了您的问题?