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 loop เล็กน้อย ผลลัพธ์จะกลายเป็นการจัดทำเอกสารด้วยตนเอง:

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

คุณอาจต้องการเพิ่ม ID ที่คล้ายกันสำหรับเธรดของคุณที่จะพิมพ์ - ตัวอย่างเช่น แทนที่จะส่งผ่าน 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
หากคุณต้องการใช้ทั้งสองอย่างผสมผสานกันเมื่อพวกมันอาจอ้างถึงไฟล์หรือเทอร์มินัลเดียวกัน คุณจะได้รับเอาต์พุตที่เสียหาย (อาจถูกแทรกแบบสุ่ม) วิธีเดียวที่จะหลีกเลี่ยงสิ่งนี้คือการสร้าง mutex ของคุณเองที่ปกป้องทั้ง stdout และ stderr และล็อคมันทุกครั้งที่คุณต้องการเขียนถึงอันใดอันหนึ่ง - person R.. GitHub STOP HELPING ICE; 05.11.2012