.NET 3.5 C # не предлагает то, что мне нужно для блокировки: подсчет асинхронных сохранений до 0 снова - PullRequest
2 голосов
/ 23 апреля 2010

У меня есть несколько записей, которые я хочу сохранить в базе данных асинхронно. Я организую их в партии, а затем отправляю. Со временем партии обрабатываются.

Тем временем пользователь может работать дальше. Есть некоторые критические операции, от которых я хочу заблокировать его, пока любой пакет сохранения все еще выполняется асинхронно.

Сохранение выполняется с использованием TableServiceContext и метода .BeginSave () - но я думаю, что это не имеет значения.

Я хочу, чтобы при каждом асинхронном сохранении увеличивалось количество блокировок, а когда оно завершалось, уменьшалось счетчик блокировок, так что он будет равен нулю, как только все закончится. Я хочу заблокировать критическую операцию, пока счетчик не равен нулю. Кроме того, я хочу квалифицировать блокировку - например, бизнес-объектом.

Я не нашел метод блокировки .NET 3.5 c #, который бы удовлетворял этому требованию. Семафор не содержит метод для проверки, если счетчик равен 0. В противном случае подойдет семафор с неограниченным максимальным количеством.

Ответы [ 3 ]

3 голосов
/ 23 апреля 2010

На самом деле Semaphare имеет метод для проверки, равен ли счетчик нулю. Используйте метод WaitOne с нулевым тайм-аутом. Он вернет значение, указывающее, был ли получен семафор. Если он возвращает false, то он не был получен, что означает, что его счет равен нулю.

var s = new Semaphore(5, 5);

while (s.WaitOne(0))
{
  Console.WriteLine("acquired");  
}

Console.WriteLine("no more left to acquire");
1 голос
/ 23 апреля 2010

Я предполагаю, что когда вы говорите «заблокировать пользователя», это не буквальная «блокировка», в то время как операции завершаются, так как это блокирует поток пользовательского интерфейса и останавливает приложение при обнаружении блокировки. Я предполагаю, что вы имеете в виду, что какое-то состояние можно проверить, чтобы элементы управления пользовательского интерфейса можно было отключить / включить.

Можно использовать что-то вроде следующего кода без блокировки:

public class BusyState
{
    private int isBusy;

    public void SignalTaskStarted()
    {
        Interlocked.Increment(ref isBusy);
    }

    public void SignalTaskFinished()
    {
        if (Interlocked.Decrement(ref isBusy) < 0)
        {
            throw new InvalidOperationException("No tasks started.");
        }
    }

    public bool IsBusy()
    {
        return Thread.VolatileRead(ref isBusy) > 0;
    }
}

public class BusinessObject
{
    private readonly BusyState busyState = new BusyState();

    public void Save()
    {
        //Raise a "Started" event to disable UI controls...

        //Start a few async tasks which call CallbackFromAsyncTask when finished.

        //Start task 1
        busyState.SignalTaskStarted();

        //Start task 2
        busyState.SignalTaskStarted();

        //Start task 3
        busyState.SignalTaskStarted();
    }

    private void CallbackFromAsyncTask()
    {
        busyState.SignalTaskFinished();

        if (!busyState.IsBusy())
        {
            //Raise a "Completed" event to enable UI controls...
        }
    }
}

Аспект подсчета заключен в BusyState, который затем используется в бизнес-объекте для сигнализации запуска и остановки задач. Возбуждение запущенных и завершенных событий может быть связано с реализацией включения и выключения элементов управления пользовательского интерфейса для блокировки пользователя во время завершения асинхронных операций.

Очевидно, что здесь есть множество предостережений для обработки состояний ошибок и т. Д. Так что просто базовый набросок кода ...

0 голосов
/ 23 апреля 2010

Какова цель подсчета блокировок, если единственная логика подразумевает, является ли значение ненулевым?

Если вы хотите сделать это для каждого типа, вы можете воспользоваться следующим подходом:

public class BusinessObject1
{
    private static readonly object lockObject = new object();

    public static object SyncRoot { get { return lockObject; } }
}

(следуя той же схеме для других бизнес-объектов)

Если вы затем заключите сохранение и критические операции в блок, подобный этому:

lock(BusinessObject1.SyncRoot)
{
    // do work
}

Вы сделаете сохранение и критические операции взаимоисключающими задачами.

Поскольку вы хотели, чтобы он был гранулированным, вы можете каскадировать замки следующим образом:

lock(BusinessObject1.SyncRoot)
lock(BusinessObject2.SyncRoot)
lock(BusinessObject3.SyncRoot)
{
    // do work
}
...