C# C石英赛道条件
我正在自动化我的网站上的一些任务,但我目前被卡住了C# C石英赛道条件,c#,multithreading,quartz.net,race-condition,C#,Multithreading,Quartz.net,Race Condition,我正在自动化我的网站上的一些任务,但我目前被卡住了 public void Execute(JobExecutionContext context) { var linqFindAccount = from Account in MainAccounts where Account.Done == false select
public void Execute(JobExecutionContext context)
{
var linqFindAccount = from Account in MainAccounts
where Account.Done == false
select Account;
foreach (var acc in linqFindAccount)
{
acc.Done = true;
// stuff
}
}
问题是,当我启动多个线程时,第一个线程被分配到同一个第一个帐户,因为它们同时将Done值设置为true。我该如何避免这种情况
编辑:
前两个线程被分配给第一个帐户,即使列表中包含30个帐户
谢谢。使用
private static readonly object locker = new object();
而不是
private object locker = new object();
您的问题是,延迟执行发生在启动foreach循环时。因此,结果被缓存,而不是在每个循环中重新计算。所以每个线程都将使用它自己的项目列表。因此,当一个帐户设置为“完成”时,另一个列表中的对象仍然保留
在这种情况下,队列更合适。只需将项目放入共享队列中,让循环获取队列中的项目,并在队列为空时让它们结束。代码中存在一些问题: 1假设您使用无状态Quartz作业,那么您的锁没有任何用处。Quartz每次触发触发器时都会创建新的作业实例。这就是为什么您会看到同一个帐户被处理了两次。只有使用statefuljob和statefuljob时,它才会起作用。或者将锁设为静态,但请继续阅读 2即使1是固定的,它也无法达到拥有多个线程的目的,因为它们都将在同一个锁上等待对方。您最好有一个线程来执行此操作 我对需求了解不够,尤其是//方面的情况。也许您不需要在多个线程上运行此代码,顺序执行就可以了。我假设情况并非如此,您希望多线程运行它。最简单的方法是只做一份工作。在这个作业中,分块加载帐户,比如说每个块中有100个作业。如果你有500个帐户,这将给你5块。将每个块处理卸载到。它将考虑使用最佳线程数。这将是一个穷人的生产者消费者队列
public void Execute(JobExecutionContext context) {
var linqFindAccount = from Account in MainAccounts
where Account.Done == false
select Account;
IList<IList<Account>> chunks = linqFindAccount.SplitIntoChunks(/* TODO */);
foreach (IList<Account> chunk in chunks) {
ThreadPool.QueueUserWorkItem(DoStuff, chunk);
}
}
private static void DoStuff(Object parameter) {
IList<Account> chunk = (IList<Account>) parameter;
foreach (Account account in chunk) {
// stuff
}
}
通常,对于多线程,在访问可变共享状态时必须非常小心。您必须确保在“DoStuff”方法中所做的一切不会导致不希望的副作用。你可能会发现它很有用
试试上面的方法。您能详细介绍一下shoydl到底使用了什么执行方法吗?是否只更新已找到帐户的“完成”属性?为什么需要多个线程?您将需要某种类型的锁对象。请参阅可能更好的链接,但这是我发现的第一个@sllev I更新已完成帐户的done属性,问题是我同时启动了约20个线程,并且前2-3个线程被分配给同一个第一帐户,因为它们同时启动,同时done值设置为false,我需要多个线程,因为它更快。枚举列表和设置属性的多个线程更快?到底什么更快?我建议坚持单身thread@sllev显然,您错过了//stuff注释。我做的更多,因此多个线程更快,因为多个帐户同时完成。这个链接可能有用,在这种情况下我们不需要多线程
public void Execute(JobExecutionContext context) {
var linqFindAccount = from Account in MainAccounts
where Account.Done == false
select Account;
IList<IList<Account>> chunks = linqFindAccount.SplitIntoChunks(/* TODO */);
foreach (IList<Account> chunk in chunks) {
ThreadPool.QueueUserWorkItem(DoStuff, chunk);
}
}
private static void DoStuff(Object parameter) {
IList<Account> chunk = (IList<Account>) parameter;
foreach (Account account in chunk) {
// stuff
}
}
foreach (var acc in linqFindAccount)
{
string mailComponent = acc.Mail;
Console.WriteLine(context.JobDetail.Name + " assigned to " + mailComponent);
acc.Done = true;
// stuff
}