fprintf()线程安全吗?

时间:2017-12-21 18:27:59

标签: c windows multithreading

我正在为一些用餐野人问题的变量写一个C解决方案。 现在,我创建线程,每个线程获得一个FILE *到同一个调试文件;在线程内我正在使用fprintf()进行一些打印。打印的声明不受任何类型的互斥锁保护。

我没有在调试文件中观察到任何交错行为;它似乎是线程安全的,虽然无处在线我发现了一个确定的声明,就是这种情况。

我所看到的是以下内容:

  1. 在Unix中,fprintf是线程安全的
  2. 使用c ++ 11编译器fprintf需要是线程安全的
  3. 我之所以要问这是因为这是一项有效的大学任务,但我仍然怀疑在另一台基于Windows的计算机中,由于上面讨论的不确定性,该程序可能会导致问题。

    我将附上线程代码,以便您看到prinintg不受保护:

        DWORD WINAPI RoomateThread(LPVOID lpParam) {
        /*=================================================
        "RoomateThread" this is the roomate thread handler function where
        the thread logic is implemented
        Input:  1. lpParam holds a roomate, runtime and pointers to both files
        Output: 1. return an DWORD value {0}->Success {-1}->Failure
                2. A code telling if the run was successful (debug file)
        A roommate follows this logic after wake up:
            1. If there are clothes available in the closet:
                a. Wait for mutex to be available, take it
                b. Check if the basket is full
                    b.1. If it is - start the machine and wait for it to finish
                c. Throw an item in the basket
                d. release mutex
            2. If the closet is empty, wait for laundry_is_empty signal, then goto (1.a)
        =================================================*/
        /*variable declerations*/
        DWORD wait_res, delta;
        BOOL release_res;
        LONG previous_count;
        roomate_thread  *elem;
        elem = (roomate_thread*)lpParam;
        /*thread logic*/
        while (TRUE) {
            /*calculate the delta between the total run time and the time the roomate had run so far*/
            delta = total_time - elem->run_time;
            /*wait until the minimum between period Ti and delta*/
            Sleep(min(elem->roomate->run_time,delta));
            fprintf(elem->debug, "RoomateThread(): Line %d, roomate %d: slept for %d mili sec, starting...\n", __LINE__, elem->roomate->roomate_id, min(elem->roomate->run_time, delta));
            /*as instructed, each roomate is active since the wakeup*/
            fprintf(elem->report, "\nRoomate %d active", elem->roomate->roomate_id);
            /*update the element total run time since start*/
            elem->run_time = elem->run_time + min(elem->roomate->run_time, delta);
            if (time_to_stop < elem->run_time) {
                /*if the element total run time is bigger then the global variable update the global*/
                time_to_stop = elem->run_time;
            }
            /*its time to close the thread properly*/
            if (time_to_stop == total_time) {
                /*if the laundry basket has clothes in it, and the roomate run as much as the total time
                activate rhe robot once more and exit*/
                if (elem->run_time == total_time && items_in_laundry!=0) {
                    release_res = ReleaseSemaphore(
                        laundry_is_full,
                        1,
                        &previous_count);
                    if (release_res == FALSE) {
                        fprintf(elem->debug, "MachineThread(): Line %d, released semaphore 'laundry_is_full' failed\nthe last error is: 0X%x\n", __LINE__, GetLastError());
                        return FAILURE;
                    }
                }
                break;
            }
            /*checks that the roomate has clothes in the closet to continue*/
            if (elem->roomate->clothes_in_laundry < elem->roomate->clothes-1) {/*roomate has clothes available*/
                fprintf(elem->debug, "RoomateThread(): Line %d,  roomate id= %d, number of dirty clothes=%d, number of total dirty clothes=%d\n",__LINE__, elem->roomate->roomate_id, elem->roomate->clothes_in_laundry, items_in_laundry);
            }
            // It's empty:
            else {
                /*waits until one of the roomates will activate the robot, cause there is no clothes in the roomate closet*/
                fprintf(elem->debug, "DAVIDS roomate %d have no clothes, waiting!!!\n", elem->roomate->roomate_id);
                elem->roomate->closet_empty = TRUE;
                /*Wait until the machine is done*/
                wait_res = WaitForSingleObject(laundry_is_empty, INFINITE);
                fprintf(elem->debug, "RoomateThread(): Line %d, roomate %d have clothes,finish waiting!!!\n",__LINE__, elem->roomate->roomate_id);
                if (wait_res != WAIT_OBJECT_0) {
                    fprintf(elem->debug, "RoomateThread() error: Line %d, waiting for sempahore 'laundry_is_empty' failed\nthe last error is: 0X%x\n", __LINE__,GetLastError());
                    return FAILURE;
                }
                fprintf(elem->debug, "RoomateThread(): Line %d, laundry_is_empty semaphore aquired , roomate: %d\n", __LINE__,elem->roomate->roomate_id);
            }
                /* Wait for mutex (machine start and clothes add "rights")*/
                wait_res = WaitForSingleObject(mutex, INFINITE);
                if (wait_res != WAIT_OBJECT_0) {
                    fprintf(elem->debug, "RoomateThread() error: Line %d, waiting for 'mutex' failed\nthe last error is: 0X%x\n", __LINE__,GetLastError());
                    return FAILURE;
                }
                fprintf(elem->debug, "RoomateThread(): Line %d, mutex aquired , roomate: %d\n", __LINE__, elem->roomate->roomate_id);
                fprintf(elem->debug, "RoomateThread(): Line 200, mutex aquired , roomate: %d\n",elem->roomate->roomate_id);
                /*Check if basket it full*/
                if (items_in_laundry == total_items) {
                /*Start Machine*/
                release_res = ReleaseSemaphore(
                        laundry_is_full,
                        1,
                        &previous_count);
                if (release_res == FALSE) {
                    fprintf(elem->debug, "MachineThread(): Line %d, released semaphore 'laundry_is_empty' failed\nthe last error is: 0X%x\n", __LINE__, GetLastError());
                    return FAILURE;
                }
                fprintf(elem->debug, "RoomateThread(): Line 210, released semaphore 'laundry_is_full' last  count is: %ld\n", previous_count);
                /*Wait for it to finish*/
                wait_res = WaitForSingleObject(laundry_is_empty, INFINITE);
                if (wait_res != WAIT_OBJECT_0) {
                    fprintf(elem->debug, "RoomateThread() error: Line %d, waiting for sempahore 'laundry_is_empty' failed\nthe last error is: 0X%x\n", __LINE__, GetLastError());
                    return FAILURE;
                }
                items_in_laundry = 0;
            }
            /*Throw in a dirty cloth*/
            elem->roomate->clothes_in_laundry++;
            items_in_laundry++;
            /*Release the mutex*/
            release_res = ReleaseMutex(mutex);
            if (release_res == FALSE) {
                fprintf(elem->debug, "RoomateThread(): Line %d, released 'mutex' failed\nthe last error is: 0X%x\n", __LINE__, GetLastError());
                return FAILURE;
            }
            fprintf(elem->debug, "RoomateThread(): Line %d, mutex released , roomate: %d\n", __LINE__, elem->roomate->roomate_id);
        }
        fprintf(elem->debug, "RoomateThread(): Line %d, thread of roomate %d ended\n", __LINE__, elem->roomate->roomate_id);
        return SUCSSES;
    }
    

    只需重新构建,这将使用visual 2015在Windows上运行

    会感激一些帮助!!!

    **如果您需要更多代码,我会添加,但其余的不是提问的信息

2 个答案:

答案 0 :(得分:3)

C2011 - 首先承认线程存在的标准的第一个版本 - 不限制fprintf()在不同线程中调用的方式可能或确实相互作用。从这个意义上讲,fprintf() 是线程安全的。

但是,POSIX确实指定来自同一进程的不同线程的fprintf()调用不会相互干扰,如果它们都指定了相同的目标文件,则它们的输出将不会混合。因此,符合POSIX的fprintf()在这个意义上是线程安全的。

我不能说标准C ++是否存在要求fprintf()具有线程安全性的要求。我会发现这令人惊讶,但它可能是真的。可以肯定的是,从多个线程写入iostream对象是安全的,但这并不意味着fprintf()也是如此。

但是,如果您询问的是Windows C或C ++,那么这一点并不重要,但是,众所周知,C(特别是C)是不合格的。如果您想了解Windows的fprintf(),请that has already been answered here(是)。

答案 1 :(得分:0)

在我看来,输出可能会混杂在一起:

来自GNU documentation

功能:int fprintf(文件*流,常量字符*模板等)

Preliminary: | MT-Safe locale | AS-Unsafe corrupt heap | AC-Unsafe mem lock corrupt | See POSIX Safety Concepts.

This function is just like printf, except that the output is written to the stream stream instead of stdout.

和“ MT安全”定义(强调我的意思):

在出现以下情况时,可以安全地调用

MT安全或线程安全功能 其他线程。 MT-Safe中的MT代表多线程。

成为MT-Safe并不意味着功能是原子的,也不意味着它使用POSIX向用户公开的任何内存同步机制。 甚至有可能依次调用MT-Safe功能 不会产生MT安全组合。例如,进行线程调用 两种MT-Safe功能,一个接一个,不保证 等同于两者结合的原子执行的行为 函数,因为其他线程中的并发调用可能会干扰 破坏性的方式。

可以在库中内联函数的整个程序优化 接口可能会暴露不安全的重新排序,因此执行内联 不建议跨GNU C库接口使用。有据可查 在整个程序优化中不能保证MT安全状态。 但是,用户可见标头中定义的功能旨在 可以安全地进行内联。