Параллельное программирование: взаимодействие процессов и нитей

Использование программного канала для передачи данных между процессами: pipe

Для иллюстрации передачи данных между процессами рассматривается простейшая игра крестики-нолики, реализованная в виде оконной программы над X-Window, тексты проекта в архиве "XO.zip". Исходный процесс создает два односторонних программных канала (канал крестиков и канал ноликов) с помощью двух вызовов pipe:

int fdX[2], fdO[2]; if (pipe(fdX) < 0) { perror("Pipe X error"); exit(-1); } if (pipe(fdO) < 0) { perror("Pipe O error"); exit(-1); } Затем процесс раздваивается с помощью вызова fork: int pid = fork(); if (pid < 0) { perror("Fork error"); exit(-1); } Родительский процесс, отвечающий за игру крестиками, в дальнейшем использует первый канал fdX для чтения, а второй канал fdO для передачи ходов партнеру по игре. Соответственно процесс-ребенок, отвечающий за игру ноликами, использует первый канал fdX для передачи, а второй канал fdO для чтения ходов. Поэтому процессы первым делом закрывают неиспользуемые концы каждой трубы, а номера каналов для чтения и записи запоминаются в переменных-членах readChannel и writeChannel класса MyWindow, представляющего окно программы на экране: if (pid != 0) { // Parent process close(fdO[0]); close(fdX[1]); w.readChannel = fdX[0]; w.writeChannel = fdO[1]; w.IAmX = true; // I am X } else { // Child process (I am O) close(fdO[1]); close(fdX[0]); w.readChannel = fdO[0]; w.writeChannel = fdX[1]; } Затем каждый из процессов создает окно, в котором отображается поле игры char field[3][3] в крестики-нолики размером 3x3. При щелчке мыши в родительском процессе в соответствующую клетку ставится крестик, и информация о сделанном ходе передается парному процессу. В процессе-ребенке в клетку ставится нолик. Логическая переменная-член IAmX графического окна указывает, является ли данный процесс родительским, т.е. играет ли он крестиками. // Process mouse click void MyWindow::onButtonPress(XEvent& event) { int x = event.xbutton.x; int y = event.xbutton.y; int ix = x / DX; // Coordinates of field cell int iy = y / DY; int c = 1; // Cross if (!IAmX) c = 2; // or Null field[ix][iy] = c; // Put cross/null into a cell char line[3]; line[0] = (char) ix; line[1] = (char) iy; line[2] = (char) c; if (!finished) { // Send data to peer process int res = write(writeChannel, line, 3); if (res < 0) { finished = true; printf("Write: res=%d (connection broken)\n", res); } } redraw(); } Для приема сделанного хода от парного процесса используется функция select, которая вызывается в цикле обработки событий, когда очередь оконных событий пуста. Функция select реализует псевдо-асинхронный режим ввода-вывода (если данные еще не пришли, функция завершается по таймауту и, таким образом, не блокирует работу графической программы). Используемый таймаут равен 0.05 сек: // Message loop XEvent e; while (GWindow::m_NumCreatedWindows > 0) { if (GWindow::getNextEvent(e)) { GWindow::dispatchEvent(e); } else { int maxFD = w.readChannel; fd_set readSet; FD_ZERO(&readSet); FD_SET(w.readChannel, &readSet); fd_set* r = &readSet; if (w.finished) { maxFD = 0; r = NULL; } timeval dt; // Timeout dt.tv_sec = 0; dt.tv_usec = 50000; // Maximal sleeping time 0.05 sec int res = select(maxFD + 1, r, 0, 0, &dt); if (res > 0) { printf("Select: res=%d\n", res); w.readMove(); // Read information sent by peer process } } } Если фунция select возвращает положительное число, то это означает, что пришли данные от парного процесса, т.е. информация о сделанном ходе. Эти данные считываются в методе readMove, в игровом поле ставится соответствующий знак и окно перерисовывается: void MyWindow::readMove() { if (!finished) { char move[3]; int res = read(readChannel, move, 3); if (res == 3) { int x = move[0]; int y = move[1]; int c = move[2]; field[x][y] = (char) c; redraw(); } else if (res <= 0) { // End of file or read error finished = true; printf("Read: res=%d (connection broken).\n", res); } } }

Параллельное программирование: нити

Два простейших примера на создание и синхронизацию нитей

Обе программы содержатся в архиве Threads.zip

Список задач.