Проблемы с потоками
От: Ummon Россия  
Дата: 21.10.10 09:45
Оценка:
Доброго времени суток!

Стоит задача сделать отправку определенных данных раз в, допустим, полчаса. При этом должна быть возможность сделать отправку принудительно.
Для этого сделал отдельный синглтон класс ThreadManager, который помимо методов старта/прибития/etc потоков содержит еще два таких метода:
  Скрытый текст
public void ForceSendUpdates()
{
    mSecondsTillSendUpdates = 0;
}

private static object mLocker = new object();
private void SendUpdates() // к этому методу привязан сервисный поток
{
    while (true)
    {
        while (mSecondsTillSendUpdates > 0)
        {
            Thread.Sleep(1000);
            mSecondsTillSendUpdates--;
        }

        lock (mLocker)
        {
            try
            {
                SendUpdatesState = OperationState.Execute; // OperationState - мой енумчик
                Thread.BeginCriticalRegion();
                // операция отправки
                SendUpdatesState = OperationState.Success;
            }
            catch (ThreadAbortException)
            {
            }
            catch (Exception)
            {
                SendUpdatesState = OperationState.Failed;
            }
            finally
            {
                Thread.EndCriticalRegion();
                if (SendUpdatesState == OperationState.Execute) // вообще говоря, излишество. Не могу представить ни одного 
                    SendUpdatesState = OperationState.Failed;   // варианта когда бы статус до этого места остался бы Execute
            }

            mSecondsTillSendUpdates = mSendUpdatesInterval;
        }
    }
}

Есть формочка с кнопкой (принудительная отправка) и двумя лейблами: время до плановой отправки, статус операции.
Есть System.Windows.Forms.Timer, вот обработчик, повешанный на tick:
  Скрытый текст
private static object mLocker = new object();
void mSendUpdateTimer_Tick(object sender, EventArgs e)
{
    lock (mLocker)
    {
        switch (ThreadManager.Instance.SendUpdatesState)
        {
            case OperationState.Wait:
                TimeSpan tsSend = TimeSpan.FromSeconds(ThreadManager.Instance.SecondsTillSendUpdates);
                string dataSendStr = Math.Floor(tsSend.TotalMinutes) + " мин. " + tsSend.Seconds + " сек. до отправки";
                labelTime.Text = dataSendStr;
                break;

            case OperationState.Execute:
                labelStatus.Text = "выполняется";
                break;

            case OperationState.Success:
                labelStatus.Text = "успешно";
                buttonSendUpdates.Enabled = true;
                ThreadManager.Instance.SendUpdatesState = OperationState.Wait;
                break;

            case OperationState.Failed:
                labelStatus.Text = "ошибка";
                buttonSendUpdates.Enabled = true;
                ThreadManager.Instance.SendUpdatesState = OperationState.Wait;
                break;
        }
    }
}

private void buttonSendUpdates_Click(object sender, EventArgs e)
{
   if (!buttonSendUpdates.Enabled)
       return;

    buttonSendUpdates.Enabled = false;
    ThreadManager.Instance.ForceSendUpdates();
}


Периодически возникает ситуация, когда лейбл с временем обновляется (т.е. операция уже завершена), а лейбл со статусом не обновлен и кнопка остается задизабленной. Сначала грешил на доступ из нескольких потоков — все обвешал локами, задизаблил кнопку, нет, все-равно проявляется баг.
Попробовал писать лог перед каждой операцией и в тредМенеджере и в форме: все выглядит более чем корректно. Последовательность статусов совпадает как для корректной работы так и для некорректной.
В чем может быть ошибка?? Я уже все перепробовал, как такая @#$%& может происходить не понимаю
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.