C # Кварцевые условия гонки - PullRequest
1 голос
/ 13 августа 2011

Я автоматизирую некоторые задачи на своем сайте, но в данный момент я застрял.

    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.Как мне избежать этого?

РЕДАКТИРОВАТЬ:

    private  object locker = new object();

    public void Execute(JobExecutionContext context)
    {
        lock (locker)
        {
            var linqFindAccount = from Account in MainAccounts
                                  where Account.Done == false
                                  select Account;

            foreach (var acc in linqFindAccount)
            {
                Console.WriteLine(context.JobDetail.Name + " assigned to " + acc.Mail);
                acc.Done = true;
                // stuff
            }

         }
     }


 Instance [ 2 ] assigned to firstmail@hotmail.com
 Instance [ 1 ] assigned to firstmail@hotmail.com

Первые два потока были назначены на первый аккаунт, хотя список содержит 30 аккаунтов.

Спасибо.

Ответы [ 4 ]

1 голос
/ 13 августа 2011

Использование

private static readonly object locker = new object();

вместо

private  object locker = new object();
0 голосов
/ 15 октября 2013
 foreach (var acc in linqFindAccount)
        {
            string mailComponent = acc.Mail;
            Console.WriteLine(context.JobDetail.Name + " assigned to " + mailComponent);
            acc.Done = true;
            // stuff
        }

Попробуйте выше.

0 голосов
/ 14 августа 2011

Мало проблем с вашим кодом:

1) Если вы используете задания Quartz без сохранения состояния, ваша блокировка не принесет пользы.Кварц создает новый экземпляр задания каждый раз, когда запускает триггер.Вот почему вы видите, что один и тот же аккаунт обрабатывается дважды.Это будет работать, только если вы используете работу с состоянием (IStatefulJob).Или сделайте блокировку статической, но продолжайте читать.

2) Даже если 1) зафиксирован, это лишит цели наличия нескольких потоков, поскольку все они будут ожидать друг друга в одной и той же блокировке.С таким же успехом у вас может быть одна нить.

Я недостаточно знаю о требованиях, особенно о том, что происходит в // stuff.Возможно, вам не нужен этот код для запуска в нескольких потоках, и последовательное выполнение будет работать нормально.Я предполагаю, что это не так, и вы хотите запустить его в несколько потоков.Самый простой способ - иметь только одну работу в Кварце.В этом задании загрузите учетные записи в чанках, скажем, 100 заданий в каждом чанке.Это даст вам 5 кусков, если у вас есть 500 учетных записей.Перенести каждую обработку чанка в пул потоков .Он позаботится об использовании оптимального количества потоков.Это была бы Очередь Производителя Потребителя бедного человека.

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, не вызовет нежелательных побочных эффектов.Вы можете найти это и это полезным.

0 голосов
/ 14 августа 2011

Ваша проблема в том, что отложенное выполнение происходит при запуске цикла foreach. Таким образом, результат кэшируется и не переоценивается каждый цикл. Таким образом, каждый поток будет работать со своим списком элементов. Поэтому, когда для учетной записи установлено значение «Готово», другой список все еще остается с объектом в нем.

A Queue больше подходит в этом случае. Просто поместите элементы в общий Queue и позвольте циклам взять элементы очереди и дать им закончить, когда Queue пуст.

...