Вечер добрый! Большая к Вам просьба объяснить в чем тут суть =)
Имеется следующая простенькая програмка:
-----------------------------------------------------------
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
#include <signal.h>sem_t A,B,C,AB; //Semaphores for all details and module
pthread_t pidA, pidB, pidC, pidM,pidEnd; //ID for all developments
int number, wMaked; //Counter of widgetsint sig(int signum) {
/* pthread_canpcel(pidA);
pthread_cancel(pidB);
pthread_cancel(pidC);
pthread_cancel(pidM);
pthread_cancel(pidEnd);*/
printf("A=%d\n",A.sem_count);
printf("B=%d\n",B.sem_count);
printf("C=%d\n",C.sem_count);
exit();
}//Make A detail for 1
void* makeA(void* t)
{
for(;;) {
sleep(1);
sem_post(&A);
write(1,"One more A\n",11);
}
}//Make B detail for 2
void* makeB(void* t)
{
for(;;) {sleep(2);
sem_post(&B);
write(1,"One more B\n",11); }
}void* makeC(void* t)
{
for(;;)
{
sleep(3);
sem_post(&C); //Make C detail for 3
write(1,"One more C\n",11);
}
}void* makeModule(void* t)
{
for(;;)
{
sem_wait(&A);
sem_wait(&B); //Waiting for A and B and then make C
sem_post(&AB);
write(1,"One more module!\n",17);
}
}//Collect all details
void* Collect(void* t)
{
for(;;)
{
sem_wait(&AB); //Wait for all details
sem_wait(&C); //Wait for C
write(1,"Widget already made!\n",21); //Report about widget
wMaked++; //Increment widget's counter
}
}void main() {
signal(SIGINT,sig);
sem_init(&A,0,0); sem_init(&B,0,0); sem_init(&C,0,0); //Init of semaphores
pthread_create(&pidA, NULL, &makeA,NULL); //Run A development
pthread_create(&pidB, NULL, &makeB,NULL); //Run B development
pthread_create(&pidC, NULL, &makeC,NULL); //Run C development
pthread_create(&pidM, NULL, &makeModule,NULL); //Run M development
pthread_create(&pidEnd, NULL, &Collect,NULL); //Run Collect development
pthread_exit(NULL);
}
-----------------------------------------------------------
Весьма примитивная - иммитатор фабрики, детали A,B и С создаются параллельно, и соответственно собираются в целое, когда по одной A, B и С уже создано... Программа останавливается по ctrl+C и, как написано в функции sig(int signum) должна выводить оставшиеся, неиспользованные детали... она вроде-как это делает, но почему-то при проверке фраза "One more A" встречается например 4 раза, "Widget already made" 2 раза, т.е по идее A должно остаться 2 штуки... а пишет например 3... или 1 итд.
В чем проблема и как это исправить? Заранее спасибо!
>Вечер добрый! Большая к Вам просьба объяснить в чем тут суть =)
>
>Имеется следующая простенькая програмка:
>В чем проблема и как это исправить? Заранее спасибо!Студент-троечник сдает курсовую? :-)
В чем проблема - не скажу из вредности, а исправить это элементарно:
в void* makeModule(void* t) - поменять местами строки sem_wait(&A); и sem_wait(&B);
в void* Collect(void* t)- соответственно sem_wait(&AB); и sem_wait(&C);PS: в данном виде программа нерабочая - как минимум, отсутствует инициализация семафора AB
PPS: Еще необходимо помнить, что часть деталей A и B находится в виде модулей AB
>Студент-троечник сдает курсовую? :-)
Нет, студент-троечник получает зачет =)>в void* makeModule(void* t) - поменять местами строки sem_wait(&A); и sem_wait(&B);
>в void* Collect(void* t)- соответственно sem_wait(&AB); и sem_wait(&C);
Странно, мне казалось от перестановки слагаемых сумма не меняется... видимо это работает как-то иначе... объясните все же пожалуйста! =)
>В чем проблема - не скажу из вредности, а исправить это элементарно:
>в void* makeModule(void* t) - поменять местами строки sem_wait(&A); и sem_wait(&B);
>в void* Collect(void* t)- соответственно sem_wait(&AB); и sem_wait(&C);
Объясните пожалуйста, как сделать так, чтобы порядок был не важен...
Ооооочень нужно - зачет.... =(
>>В чем проблема - не скажу из вредности, а исправить это элементарно:
>>в void* makeModule(void* t) - поменять местами строки sem_wait(&A); и sem_wait(&B);
>>в void* Collect(void* t)- соответственно sem_wait(&AB); и sem_wait(&C);
>Объясните пожалуйста, как сделать так, чтобы порядок был не важен...
Чтобы порядок был неважен, Вам нужно добиться атомарного с точки зрения выводящих результат процедур, изменения комлектов счетчиков по всей програмульке. Если это делать по-честному, нужно переписывать. Грязный хак для Вашего конкретного случая я уже привел. Проблема программы в изначальном виде - гарантированная рассинхронизация изменения значений семафоров со стороны строк printf("A=%d\n",A.sem_count);.
В треде с makeModule исполнение идет пачкой
sem_wait(&B);
sem_post(&AB);
sem_wait(&A);
- именно в такой последовательности, т.к. последовательность подачи деталей такова, что тред будет все время висеть на семафоре &B. посему и набегает лишний sem_wait(&A);
в треде с Collect - ситуация аналогична.
>Чтобы порядок был неважен, Вам нужно добиться атомарного с точки зрения выводящих
>результат процедур, изменения комлектов счетчиков по всей програмульке. Если это делать
>по-честному, нужно переписывать. Грязный хак для Вашего конкретного случая я уже
>привел. Проблема программы в изначальном виде - гарантированная рассинхронизация изменения значений
>семафоров со стороны строк printf("A=%d\n",A.sem_count);.
>В треде с makeModule исполнение идет пачкой
>sem_wait(&B);
>sem_post(&AB);
>sem_wait(&A);
>- именно в такой последовательности, т.к. последовательность подачи деталей такова, что тред
>будет все время висеть на семафоре &B. посему и набегает лишний
>sem_wait(&A);
>в треде с Collect - ситуация аналогична.
А как требуется переписать? Косяк в том, что в makeModule входим допустим, делаем sem_wait(&A), затем начинает sem_wait(&B) и по ctrl+C падает... в итоге sem_wait уменьшил значение A на единицу, но модуль не создал... И в итоге теряется... как сделать, чтобы порядок был не важен? Флаговые переменные заводить нельзя ибо ctrl+c по идее может прервать между sem_post и изменением флага...
Объясните все-же пожалуйста, как изменить прогу, чтобы было корректно?
>Объясните все-же пожалуйста, как изменить прогу, чтобы было корректно?
Вообще, я не являюсь знатоком IPC, но могу предложить следующий метод:
1. Для счетчиков вместо семафоров используются глобальные переменные, закрытые мутексом(мутексами).
2. Для коммуникации между тредами используется posix messaging: 1-я очередь makeModule() "слушает" приход деталей от MakeA() и МакеB(), 2-я - Collect() собирает сообщения ов AB и C.
3. Обработчик SIGINT _НЕ_ производит вывод/выход, а устанавливает флаг выхода/посылает сообщения о выходе.
4. Треды производят обработку информации из п.3. и завершаются _САМОСТОЯТЕЛЬНО_.
5. Вывод числа деталей производится в main() по окончанию работы всех тредов.
Вероятно, существуют и более простые/красивые/быстрые варианты, но и такой должен работать корректно.
Остались же еще маньяки, пишушие на Си с использованием голого API :)
>Остались же еще маньяки, пишушие на Си с использованием голого API :)Смотря где применяться будет. Если нужна мелкая везде собирающаяся
программулина, стоит потратить на неё дополнительные 30 минут и
не таскать вместе с ней горы компиляторов/рантаймов/просто библиотек.
Как Вам, например, такое (собирается везде и везде работает, никаких
доп. плясок с бубном с настройками сетевых сервисов для пересылки
файлика через сеть):#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>#ifdef WIN32
# include <winsock2.h>
# include <windows.h>#define sleep(n) SleepEx((n)*1000, FALSE);
typedef int ssize_t;#else
# include <sys/types.h>
# include <sys/socket.h>
# include <netinet/in.h>
# include <arpa/inet.h>
# include <netdb.h>
# include <unistd.h>
# include <strings.h>#define closesocket close
extern int h_errno;#endif
#define BUF_SIZE 32768static int do_resolve_addr(const char* host, struct sockaddr_in* addr_in)
{
struct hostent *hp = NULL;
do {
errno = 0;
hp = gethostbyname(host);
} while ( hp==NULL && (h_errno==TRY_AGAIN || errno==EINTR) );
if ( hp==NULL )
return -1;
memcpy(&addr_in->sin_addr, *(hp->h_addr_list),
sizeof(addr_in->sin_addr.s_addr));
return 0;
}static int do_make_address(const char* addr, struct sockaddr_in* addr_in)
{
char *new_host, *new_port;
new_host = strdup(addr);
new_port = strstr(new_host, ":");
if ( new_port )
{
*new_port = '\0';
new_port++;
}
else
new_port = "9999";
addr_in->sin_family = AF_INET;
addr_in->sin_port = htons((unsigned short)atoi(new_port));
addr_in->sin_addr.s_addr = inet_addr(new_host);
if ( addr_in->sin_addr.s_addr == -1)
if ( do_resolve_addr(new_host, addr_in) < 0 ) {
fprintf(stderr, "netstream: cannot resolve address: %s\n", strerror(errno));
free(new_host);
return -1;
}
free(new_host);
return 0;
}
/* ------------------------------------------------------------ */
static int do_send(const char* addr, FILE* f)
{
char buf[BUF_SIZE];
struct sockaddr_in addr_in;
int sock, cnc;
size_t nbytes;
if ( do_make_address(addr, &addr_in) == -1 )
return 1;
sock = socket(AF_INET, SOCK_STREAM, 0);
if ( sock==-1 ) {
fprintf(stderr, "netstream: cannot create socket: %s\n", strerror(errno));
return 1;
}
if ( bind(sock, (struct sockaddr *)&addr_in, sizeof(addr_in)) == -1 ) {
fprintf(stderr, "netstream: cannot bind socket: %s\n", strerror(errno));
shutdown(sock, 2);
closesocket(sock);
return 1;
}
if ( listen(sock, 1) == -1 ) {
fprintf(stderr, "netstream: cannot listen: %s\n", strerror(errno));
shutdown(sock, 2);
closesocket(sock);
return 1;
}
do {
cnc = accept(sock, NULL, NULL);
if ( cnc<0 )
sleep(1);
} while (cnc<0);
shutdown(sock, 2);
closesocket(sock); sock = -1;
while ( !ferror(f) && !feof(f) ) {
nbytes = fread(buf, 1, sizeof(buf), f);
if ( nbytes<1 )
continue;
if ( send(cnc, buf, nbytes, 0) == -1 ) {
fprintf(stderr, "netstream: cannot send: %s\n", strerror(errno));
shutdown(cnc, 2);
closesocket(cnc);
return 2;
}
}
if ( ferror(f) ) {
fprintf(stderr, "netstream: file read error: %s\n", strerror(errno));
return 2;
}
shutdown(cnc, 2);
closesocket(cnc);
return 0;
}/* ------------------------------------------------------------ */
static int do_recv(const char* addr, FILE* f)
{
char buf[BUF_SIZE];
struct sockaddr_in addr_in;
int sock;
ssize_t nbytes;
if ( do_make_address(addr, &addr_in) == -1 )
return 1;
sock = socket(AF_INET, SOCK_STREAM, 0);
if ( sock==-1 ) {
fprintf(stderr, "netstream: cannot create socket: %s\n", strerror(errno));
return 1;
}
if (connect(sock, (struct sockaddr*)&addr_in, sizeof(addr_in)) == -1) {
fprintf(stderr, "netstream: unable to connect: %s\n", strerror(errno));
shutdown(sock, 2);
closesocket(sock);
return 1;
}
for (;;) {
nbytes = recv(sock, buf, sizeof(buf), 0);
if ( nbytes==0 )
break;
if ( nbytes < 0 ) {
fprintf(stderr, "netstream: unable to recv: %s\n", strerror(errno));
shutdown(sock, 2);
closesocket(sock);
return 2;
}
if (fwrite(buf, 1, nbytes, f) != (size_t)nbytes) {
fprintf(stderr, "netstream: file write failed: %s\n", strerror(errno));
shutdown(sock, 2);
closesocket(sock);
return 2;
}
}
shutdown(sock, 2);
closesocket(sock);
return 0;
}
/* ------------------------------------------------------------ */
static void usage_and_die()
{
fprintf(stderr, "USAGE:\n\tnetstream {send|receive} address [filename]\n");
exit(3);
}int main(int argc, char* argv[])
{
int ret = 0;
FILE* f = NULL;
#ifdef WIN32
WORD wVersionRequested;
WSADATA data;
wVersionRequested = MAKEWORD(1, 1);
WSAStartup(wVersionRequested, &data);
#endif
if ( argc!=3 && argc!=4 )
usage_and_die();
switch ( *argv[1] ) {
case 's': case 'S':
f = stdin;
if ( argc==4 )
f = fopen(argv[3], "rb");
break;
case 'r': case 'R':
f = stdout;
if ( argc==4 )
f = fopen(argv[3], "wb");
break;
default:
usage_and_die();
}
if ( argc==4 && f==NULL ) {
fprintf(stderr, "netstream: cannot open '%s': %s\n", argv[3], strerror(errno));
return 2;
}
switch ( *argv[1] ) {
case 's': case 'S':
ret = do_send(argv[2], f);
break;
case 'r': case 'R':
ret = do_recv(argv[2], f);
break;
}
if ( f!=stdin || f!=stdout )
fclose(f);
return ret;
}/* End Of File */
>>Остались же еще маньяки, пишушие на Си с использованием голого API :)
>
>Смотря где применяться будет. Если нужна мелкая везде собирающаяся
>программулина, стоит потратить на неё дополнительные 30 минут и
>не таскать вместе с ней горы компиляторов/рантаймов/просто библиотек.
>Как Вам, например, такое (собирается везде и везде работает, никаких
>доп. плясок с бубном с настройками сетевых сервисов для пересылки
>файлика через сеть):
>Да, честно сказать, никак. Я бы наверное уволился, но такой код сопровождать не стал. Навевает грустные мысли о 20 веке.
>Да, честно сказать, никак. Я бы наверное уволился, но такой код
>сопровождать не стал. Навевает грустные мысли о 20 веке.Без обид, но скорее - о Вашей квалификации. Ибо код простой и понятный до безобразия.