• XSS.stack #1 – первый литературный журнал от юзеров форума

Статья C++ Как писать асинхронные приложения. Часть 1

Triada

RAM
Пользователь
Регистрация
13.12.2019
Сообщения
127
Реакции
53
Форуму привет. Сейчас я расскажу как писать правильно писать асинхронные приложения под Windows.
Примером у нас будет такая задача: нам нужно сделать выполнение цикла в нескольких потоках, и при достижении определённого
количества итераций остановить потоки, незнающий человек напишет примерно такой код:
C++:
#include <windows.h>
#include <shlwapi.h>

int value = 0;

void worker() {
    while (1) {
        //do something
        value+=10;
    }
}

int main() {
    HANDLE hThreads[4];

    for (int i = 0; i < 4; i++) {
        hThreads[i] = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)worker, 0, 0, 0);
    }

    while (1) if (value >= 1000) for (int i = 0; i < 4; i++) TerminateThread(hThreads[i], 0);
}

Но ошибка тут в.... value++

Суть в том, что value++ это набор асм инструкций, а что будет если из-за того, что это мультитред эти инстуркции выполнятся по очереди.
Значение будет уже не достоверным, для этого есть атомарные Interlocked функции, в нашем случаее нам нужна InterlockedExchange

Правильная версия кода:
C++:
#include <windows.h>
#include <shlwapi.h>

volatile ULONGLONG value = 0;//volatile - флаг который говорит компилятору что переменную не нужно оптимизировать

void worker() {
    while (1) {
        //do something
        InterlockedExchange(&value, 10);
        //1 это значение на изменение, если там было-бы -1 то получился бы декримент
    }
}

int main() {
    HANDLE hThreads[4];

    for (int i = 0; i < 4; i++) {
        hThreads[i] = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)worker, 0, 0, 0);
    }

    while (1) if (value >= 1000) for (int i = 0; i < 4; i++) TerminateThread(hThreads[i], 0);
}

А вообще написание качевственного асинхронного софта очень сложное занятие.
 
Последнее редактирование:
value++ это одна asm инструкция - inc.
MOV EAX, [g_x] ; значение из g_x помещается в регистр
INC EAX ; значение регистра увеличивается на 1
MOV [g_x], EAX ; значение из регистра помещается обратно в g_x

А вот что может случиться в мультитреде:
MOV EAX, [g_x] ; поток 1: в регистр помещается 0
INC EAX ; поток 1: значение регистра увеличивается на 1
MOV EAX, [g_x] ; поток 2: в регистр помещается 0
INC EAX ; поток 2: значение регистра увеличивается на 1
MOV [g_x], EAX ; поток 2: значение 1 помещается в g_x
MOV [g_x], EAX ; поток 1: значение 1 помещается в g_x

Как говорят, думай прежде чем писать.
 
MOV EAX, [g_x] ; значение из g_x помещается в регистр
INC EAX ; значение регистра увеличивается на 1
MOV [g_x], EAX ; значение из регистра помещается обратно в g_x

А вот что может случиться в мультитреде:
MOV EAX, [g_x] ; поток 1: в регистр помещается 0
INC EAX ; поток 1: значение регистра увеличивается на 1
MOV EAX, [g_x] ; поток 2: в регистр помещается 0
INC EAX ; поток 2: значение регистра увеличивается на 1
MOV [g_x], EAX ; поток 2: значение 1 помещается в g_x
MOV [g_x], EAX ; поток 1: значение 1 помещается в g_x

Как говорят, думай прежде чем писать.
боже мой... value++ аналогично inc xxx. то что ты скинул - уже сторонние манипуляции, которые конкретно к этой строке не относятся. ты ведь сам сказал, что:

value++ это набор асм инструкций

думай прежде чем писать.
- верно подмечено.
 
Без обид, но Eternal прав...
__asm вставка будет содержать в себе инкрементирование переменной, а также прочий 'мусор', без которого в asm'e не обойтись.
Так что variable++ => inc register
 
Последнее редактирование:
Жду тогда от вас полный набор асм инструкицй которые увеличат переменную value на 1, в виде __asm вставки, иначе всё что вы говорите - балаболство
эмм... всмысле?

C:
    int value = 231;

    __asm
    {
        inc value
    }

ну типо вот, держи
 
А, бля, с инкрементом я походу обосрался. Хотя если взять не value++, а value += 10 к примеру то тогда уже нужно использовать интерлокед
 
Код плохой. Нужно, проверять результаты работы апи. Эксченж используется бессмыссленно и явно без понимания для чего он нужен.
InterlockedExchange(&value, 10); == value = 10; == mov [value], 10

список команд для которых актуален lock:
BTS, BTR, BTC mem, reg/imm
XCHG reg, mem
XCHG mem, reg
ADD, OR, ADC, SBB, AND, SUB, XOR mem, reg/imm
NOT, NEG, INC, DEC mem

для мува нет префикса лок, так как нет этом смысла.

InterlockedExchange( имел бы смысл, например в таком исполнении.
valuePrevious = InterlockedExchange(&value, 10);
 
Пожалуйста, обратите внимание, что пользователь заблокирован
Причем тут асинхронные приложения? Тут асинхронности нет и в помине, речь просто о синхронизации многопоточных приложений, и конкретно - interlocked функции (которых есть больше чем одна).
Будь я новичком, я бы ничего не понял с этой статьи. Почему ..exchange, если есть ..add ? Почему не использовать синхронизацию потоков через Wait.. функции? Ну и т.д.
В общем, автор, не пиши больше под винду, ок? Почитай сначала Рихтера.
 
Причем тут асинхронные приложения? Тут асинхронности нет и в помине, речь просто о синхронизации многопоточных приложений, и конкретно - interlocked функции (которых есть больше чем одна).
Будь я новичком, я бы ничего не понял с этой статьи. Почему ..exchange, если есть ..add ? Почему не использовать синхронизацию потоков через Wait.. функции? Ну и т.д.
В общем, автор, не пиши больше под винду, ок? Почитай сначала Рихтера.
Уже прочёл, судя по всему у меня проблемы с изложением мыслей.
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх