Обязательно ли иметь два замка на два объекта? - PullRequest
1 голос
/ 14 июля 2020

Если у меня есть два общих ресурса, эти ресурсы обновляются в их собственных отдельных задачах, которые выполняются одновременно. Вторая задача проверяет состояние общего ресурса из первой задачи, а затем обновляет собственный общий ресурс. После завершения одной из задач я проверяю состояние обоих общих ресурсов. Нужны ли мне два отдельных замка, чтобы сделать этот потокобезопасным, или одного достаточно? Например:

private void example()
{
    object lockObj = new object();

    int x = 0;
    int y =0;

    List<Task> tasks = new List<Task>();

    Task task1 = Task.Factory.StartNew(() =>
    {
        try
        {
            int z = doComputation()
            resultofComputation = z;
        }
        catch
        {
            resultofComputation=-1;
        }
        finally
        {
            lock(lockObj)
            {
                x = resultofComputation;
            }
        }
            
    }

    tasks.Add(workTask);

    Task task2 = Task.Factory.StartNew(() =>
    {
        try
        {
            checkOnstatusofThing(ref x);
            lock(lockObj)
            {
                y +=x;
            }
        }
        finally
        {

        }
    }

    Task.WaitAny(tasks.ToArray());
    if(x =3 || y ==9)
    {
        return -1;
    }
    return 0;
}

checkOnstatusofThing(ref int x)
{
    if(x == 5)
    {
        return;
    }
}

1 Ответ

1 голос
/ 16 июля 2020

Использование одного объекта блокировки - безопасный вариант. Вы определяете переменные, составляющие общее состояние, и тщательно используете блокировку каждый раз, когда вы пишете и читаете эти переменные из любого потока. Если вы сделаете это, то правильность вашего приложения будет легко доказать (легко по стандартам многопоточности, что по своей сути сложно).

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

int snapshot;
lock (lockObj)
{
    snapshot = sharedState;
}
MethodCall(snapshot);

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

...