printf не работает должным образом при использовании потоков

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

.
.
.
        while(1){

        printf("waiting for a connection\n");

        csock = (int*)malloc(sizeof(int));

        if((*csock = accept( hsock, (struct sockaddr*)&sadr, &addr_size))!= -1){
            printf("---------------------\nReceived connection from %s\n",inet_ntoa(sadr.sin_addr));
            client_counter++;
            pthread_create(&thread_id,0,&SocketHandler, (void*)csock );
        }

        else{
            fprintf(stderr, "Error accepting %d\n", errno);
        }
        }// end while
.
.
.

и это функция, которую используют потоки.

void* SocketHandler(void* lp){

    int *csock = (int*)lp;
    char buffer[1024];
    int buffer_len = 1024;
    int bytecount;
    char* str_exit="exit";

        while(1){

            memset(buffer, 0, buffer_len);

            if((bytecount = recv(*csock, buffer, buffer_len, 0))== -1){
            fprintf(stderr, "Error receiving data %d\n", errno);
            exit(0);
            }

            if(strcmp(buffer,str_exit)==0){
                break;
            }

            printf("Received bytes %d\nReceived string \"%s\"\n", bytecount, buffer);
            strcat(buffer, " SERVER ECHO");

            if((bytecount = send(*csock, buffer, strlen(buffer), 0))== -1){
            fprintf(stderr, "Error sending data %d\n", errno);
            exit(0);
            }

                //printf("Sent bytes %d Sent String %s\n", bytecount,buffer);
        }

    printf("Client disconnected\n");
    free(csock);
    return 0;
}

и вывод такой, когда клиент (поток) подключается к серверу.

waiting for a connection
---------------------
Received connection from 127.0.0.1
waiting for a connection
Client disconnected
---------------------
Received connection from 127.0.0.1
waiting for a connection
Client disconnected

когда первый клиент подключается, вывод работает правильно, но когда подключается второй, строка "waiting for a connection" идет после "Received connection". ну, это должно работать наоборот. я буду рад, если вы можете помочь, и все равно спасибо


person quartaela    schedule 04.11.2012    source источник
comment
Вероятно, вам нужно подумать о fflush(). Возможно, вам придется подумать о flockfile() и funlockfile().   -  person Jonathan Leffler    schedule 05.11.2012
comment
В чем проблема - не вижу: ожидание/получено, ожидание/получено, ожидание. Вот как вы это закодировали, и это результат, который я ожидал. Поток приема запускается, ждет, затем получает два соединения и ждет еще одно.   -  person Martin James    schedule 05.11.2012
comment
@JonathanLeffler на самом деле поставил fflush(stdout) выше и ниже printf("waiting for a connection\n");, но все равно результат был таким же   -  person quartaela    schedule 05.11.2012
comment
Это потому, что в выводе нет ничего плохого.   -  person Martin James    schedule 05.11.2012


Ответы (4)


Нет проблем. Помимо первого входа в цикл, «ожидание соединения» будет последним, что будет напечатано потоком принятия после подключения клиента.

Иными словами, этот цикл начинается/заканчивается вызовом accept(), за исключением тех случаев, когда он введен впервые. Это первый раз, когда это «исключение», а не последующие циклы.

person Martin James    schedule 04.11.2012
comment
да, наконец, я понял, что порядок верен. моя вина :) - person quartaela; 05.11.2012

setbuf(stdout,NULL);
setbuf(stderr,NULL);

Отключить буферизацию вывода. printf не входит повторно, поэтому синхронизируется с использованием глобальных блокировок. Когда вывод буферизуется, один поток может не снять блокировку до того, как другой начнет печать.

Всегда рекомендуется отключить буферизацию вывода. или вызовите fflush() вручную при многопоточности

person Anirudh Ramanathan    schedule 04.11.2012
comment
я попытался вызвать fflush(stdout) точно выше и ниже оператора printf. но ни один из них не работал - person quartaela; 05.11.2012
comment
..потому что нечего исправлять! - person Martin James; 05.11.2012

Там нет проблемы (или, по крайней мере, то, что вы описываете, не является проблемой). Вот некоторый аннотированный вывод:

waiting for a connection             # 1
---------------------                # 1
Received connection from 127.0.0.1   # 1
waiting for a connection             #   2
Client disconnected                  #        thread handling connection 1
---------------------                #   2
Received connection from 127.0.0.1   #   2
waiting for a connection             #     3
Client disconnected                  #        thread handling connection 2

Если вы немного измените свой цикл while, вывод станет самодокументируемым:

int i = 0;
while(1) {

    printf("%d: waiting for a connection\n", i);

    csock = (int*)malloc(sizeof(int));

    if((*csock = accept( hsock, (struct sockaddr*)&sadr, &addr_size))!= -1) {
        printf("%d: ---------------------\n%d: Received connection from %s\n", 
            i, i, inet_ntoa(sadr.sin_addr));
        client_counter++;
        pthread_create(&thread_id,0,&SocketHandler, (void*)csock );
    }

    else{
        fprintf(stderr, "%d: Error accepting %d\n", i, errno);
    }

    ++i;
}// end while

Возможно, вы захотите добавить аналогичный идентификатор для своего потока для вывода — например, вместо передачи одного int, представляющего сокет, передайте небольшую структуру, содержащую сокет и client_counter (или `i, или что-то более полезное) значение на момент создания потока.

person Michael Burr    schedule 04.11.2012
comment
Наконец-то это понял кто-то другой! Я думал, что это я схожу с ума :) - person Martin James; 05.11.2012
comment
Извините, ребята, я не заметил и сошел с ума. спасибо за откровенный ответ :) - person quartaela; 05.11.2012
comment
@quartaela - не нужно извиняться. Циклы, которые останавливаются посередине из-за блокирующих вызовов, — это лишь одна из тех многопоточных вещей, к которым вы скоро привыкнете :) - person Martin James; 05.11.2012

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

person R.. GitHub STOP HELPING ICE    schedule 04.11.2012
comment
есть ли способ правильно использовать их оба _? или использование только одного из них - единственный способ избежать каких-либо осложнений - person quartaela; 05.11.2012
comment
Если вы хотите смешивать использование обоих, когда они могут ссылаться на один и тот же базовый файл или терминал, вы получите поврежденный вывод (возможно, чередующийся случайным образом). Единственный способ избежать этого — создать собственный мьютекс, защищающий как stdout, так и stderr, и блокировать его всякий раз, когда вы хотите писать в любой из них. - person R.. GitHub STOP HELPING ICE; 05.11.2012