阻止并发访问的线程池
问题描述:
所以我运行一个查询和处理在返回同时使用一个名为StartJob功能,将在我的工作工作行:阻止并发访问的线程池
ThreadPool.QueueUserWorkItem(StartJob, job);
的伟大工程,是非常快。但是现在我被告知当查询返回时,某些行可能具有与job.UserID相同的值,并且我们无法同时为相同的job.UserID值运行StartJob函数。问题是:如何使StartJob块执行,直到具有相同用户ID的StartJob的任何其他实例都已完成?
我确定有一种方法可以获得每个用户ID锁,但我不知道该怎么做。谢谢您的帮助。
答
HashSet<int> hs = new HashSet<int>(); // In common with all the threads
int id = 1; // Your id
// This is the body of your Thread. You pass it the id as you want.
// A closure on it, or as a parameter of the thread.
// This will begin with short spins, every time trying to add the id to the hashset.
// SpinUntil stops when the lambda function returns true.
SpinWait.SpinUntil(() =>
{
lock (cd)
{
return hs.Add(id);
}
});
// OR, if you know the operation is slow, or < .NET 4.0
// This is clearer. The thread yields until it can add the id to the hashset.
while (true)
{
lock (hs)
{
if (hs.Add(id))
{
break;
}
}
Thread.Yield();
}
// End of the variant
// Remember the try/finally! It's important in case of exceptions!!!
try
{
// Put here your code
// Put here your code
// Put here your code
}
finally
{
lock (hs)
{
hs.Remove(id);
}
}
两个版本,一个是好短StartJob
和只适用于.NET 4.0,一个与.NET> = 3.5的作品。
显然hs
在所有线程之间是相同的,并且id
是job.UserID
。
我将在.NET 4.0中加入,您可以使用SpinLock
而不是lock
。它有点快,但它的语法有点棘手。
答
使用任务并行库
var tasks = new Dictionary<int, Task>();
QueueJob(Job job)
{
lock(tasks)
if (tasks.ContainsKey(job.UserID))
{
var newTask = tasks[job.UserID].ContinueWith(_=>StartJob(job));
tasks[job.UserID] = newTask;
}
else
tasks[job.UserID] = Task.Factory.StartNew(()=>StartJob(job));
}
将不会有并发冲突如果您用同样的job.UserID StartJob,直到它的目标是像一个文件中的一些共享资源或数据库行。你可以在StartJob中展示你在做什么? –
什么版本的.NET? 3.5还是4.0? – xanatos
@invisible完全正确,我在StartJob中运行了更多查询,锁定了UserID所拥有的行,因此我们看到超时,因为这些长时间运行的查询正在锁定这些行。所以我想用一些特定于UserID的锁来启动StartJob,并在那里*地阻止它。 – powlette