параллелизм чтения/записи nodejs

Вот простой код, демонстрирующий, что я пытаюсь сделать

myVar = 1
reader = () ->
    getDataFromServer1().then ->
        # uses myVar and does stuff according to its value
    # returns promise
writer = () ->
    getDataFromServer2().then ->
        # assigns to myVar
    # returns promise

Q.all([reader(), reader(), reader(), writer(), writer(), writer()]).done ->
    console.log 'done'

Поэтому у меня одновременно работает несколько потоков. некоторые из них изменяют значение myVar, а некоторые читают значение и полагаются на него. И я не хочу, чтобы писатель писал, пока пишет другой писатель или читает читатель. Хотя читатели могут читать одновременно. Это похоже на проблему "читатели-писатели".

Я попытался решить эту проблему, определив функцию sharedResource следующим образом.

sharedResource = (initialValue) ->
    readLock = Q.fcall ->
    writeLock = Q.fcall ->

    value: initialValue
    read: (action) ->
        newPromise = writeLock.then action
        readLock = Q.all([newPromise, readLock]).then -> null
        newPromise
    write: (action) ->
        newPromise = Q.all([readLock, writeLock]).then action
        writeLock = Q.all([newPromise, writeLock]).then -> null
        newPromise

а затем изменил мой код, чтобы использовать его

myVar = sharedResource 1
reader = () ->
    myVar.read ->
        # noone is writing to myVar while doing this request:
        getDataFromServer1().then (data) -> 
            # read myVar.value instead of myVar, e.g.
            expect(data == myVar.value)
writer = () ->
    myVar.write ->
        # noone reads or writes myVar while doing this request:
        getDataFromServer2().then (data) ->
            # change myVar.value instead of myVar, e.g.
            myVar.value = data

Q.all([reader(), reader(), reader(), writer(), writer(), writer()]).done ->
    console.log 'done'

Это отлично работало, когда у меня был только один sharedResource. Вот где возникает проблема

myVar1 = sharedResource 1
myVar2 = sharedResource 2
action1 = () ->
    myVar1.read ->
        myVar2.write ->
            getDataFromServer1().then (data) ->
                myVar2.value = data + myVar1.value
action2 = () ->
    myVar2.read ->
        myvar1.write ->
            getDataFromServer2().then (data) ->
                myVar1.value = data + myVar2.value

Q.all([action1(), action1(), action1(), action2(), action2(), action2()]).done ->
    console.log 'done'

Здесь происходит случай тупика. Каждое обещание ожидает разрешения другого. Ни одна из них не решается, и программа останавливается.

Изменить

Я постараюсь объяснить:

На самом деле это код для тестирования моего сервера. Чтобы увидеть, как это работает, когда несколько клиентов отправляют несколько запросов одновременно. Скажем, например, каждый раз, когда action1 отправляет запрос, сервер увеличивает значение, хранящееся в его базе данных. На стороне клиента (код, который вы видите) я также увеличиваю переменную, которая содержит значение, которое, как я ожидаю, будет на сервере. И затем, когда action2 отправляет запрос, сервер отвечает этим значением, и я assert значение в ответе должно совпадать с моей локальной переменной.

Поэтому я должен получить блокировку, прежде чем отправлять запрос, чтобы убедиться, что action2 не запрашивает переменную во время ее изменения.

Надеюсь, это поможет.


person Ali Dorosty    schedule 05.11.2014    source источник
comment
На днях я говорил с kriskoval о читателях-писателях! Дайте выкопать. Inb4 github.com/kriskowal/gtor   -  person Benjamin Gruenbaum    schedule 05.11.2014
comment
@BenjaminGruenbaum большое спасибо. Буду очень признателен, если получу ответ.   -  person Ali Dorosty    schedule 05.11.2014
comment
Хм, а почему вы получаете блокировки еще до получения данных с сервера? Действительно ли ваши действия чтения-записи (в обратном вызове после извлечения данных) асинхронны? Из вашего примера это не понятно.   -  person Bergi    schedule 05.11.2014
comment
@Bergi Спасибо за ваше наблюдение. Я не думал, что кто-то даже удосужится прочитать мой длинный код. Я отредактировал вопрос и добавил некоторые пояснения.   -  person Ali Dorosty    schedule 05.11.2014
comment
Хм, я не думаю, что введение сервера и нескольких клиентов делает это более ясным… Скорее, вы должны ввести в свой код переменные, которые отправляются на сервер, и параметры в ваших обратных вызовах для результатов сервера…   -  person Bergi    schedule 05.11.2014
comment
@Bergi Я думаю, что публикация моего полного кода просто запутает людей.   -  person Ali Dorosty    schedule 05.11.2014
comment
Нет, но вам нужно показать поток данных через ваши промисы. Если вы не понимаете, что я имею в виду, вы все равно можете связать полный код (и я постараюсь понять вашу проблему)   -  person Bergi    schedule 05.11.2014
comment
@Bergi Я думаю, что мой код нуждается в серьезном рефакторинге. Но вот gist.github.com/alidorosty/852e257dbb38e8f55a0f   -  person Ali Dorosty    schedule 05.11.2014
comment
Спасибо, кажется, теперь я лучше понимаю вашу ситуацию. Я также отредактировал ваш вопрос, так как надеюсь, что это поможет лучше понять ваш дизайн sharedRessource (изначально у меня были проблемы с этими action, ожидая, что они получат текущее значение в качестве параметра и вернут (обещание) значение для записи; вместо этой .value манипуляции). Можете ли вы подтвердить, что это все еще передает ваше намерение?   -  person Bergi    schedule 06.11.2014
comment
@ Берги Точно. Большое спасибо.   -  person Ali Dorosty    schedule 06.11.2014
comment
Вижу, ты еще не получил ответа, а у Крис, наверное, не было времени. Вас все еще интересует ответ?   -  person Benjamin Gruenbaum    schedule 17.12.2014
comment
@adm Извините, но я кое-что упустил. JavaScript является параллельным, но не параллельным: каждый вызов функции является атомарным, то есть then(read_is_atomic()). Единственная неприятная вещь с вашим кодом в том, что ответ не всегда сразу следует за сделанным запросом, он может где-то тормозиться и приходить позже.   -  person ilyaigpetrov    schedule 04.10.2015