Безопасен ли поток fprintf в OS X? - PullRequest
7 голосов
/ 03 февраля 2010

Безопасен ли поток fprintf в OS X? Если да, то где это задокументировано?

Ответы [ 2 ]

5 голосов
/ 19 февраля 2010

Спецификация потоков POSIX (AKA Pthreads), которой соответствует OS X, требует, чтобы функции stdio были потокобезопасными. Он также предоставляет функции flockfile и funlockfile, чтобы другие потоки не могли чередовать операции ввода-вывода на ФАЙЛЕ *, пока он заблокирован.

См. http://pubs.opengroup.org/onlinepubs/007908799/xsh/threads.html,, в частности, в разделе под заголовком " Безопасность потоков ".

3 голосов
/ 04 февраля 2010

Это был хороший вопрос, хотя подобные вопросы задавались здесь много раз. Меня интересовал аспект OSX, потому что я сам пытался освоиться с этой системой. (возможно, вам следует добавить тег OSX)

I THINK fprintf () является поточно-ориентированным в OSX. Моя первая причина этого заключается в том, что люди Дарвина шли в этом направлении, о чем свидетельствует их выбор отказаться от «старой школы» «errno» в пользу функции errno (). Для документации, просто следуйте '/usr/include/errno.h'. Без этого ни один из компонентов libc не был бы безопасен для потоков. Тем не менее, использование функции errno () ничего не доказывает о fprintf (). Это только начало. Я уверен, что всем известна хотя бы одна ситуация, когда Apple не справилась с хорошей идеей.

Еще одна причина, по которой я верю в «безопасность потоков» функции fprintf () - исходный код , который должен быть «реальным», по крайней мере, до 10.6. когда Apple закрыла (частично / полностью) OSX. Отсканируйте этот код на наличие «MT-Safe» и вы увидите CLAIM , что нелокальная версия vfprintf () является поточно-ориентированной. Опять же, это ничего не доказывает. И все же это форма документации, которую вы хотели.

Моя последняя причина полагать, что fprintf () является поточно-ориентированным, был контрольным примером. Это тоже ничего не доказывает. Возможно, это доказывает, что буферное пространство является поточно-ориентированным. ОК, это было оправданием написать небольшую программу для развлечения. На самом деле, я не написал это. Я нашел скелет онлайн и изменил его. Определение "FLUSH_BUFFER" позволяет вам более четко видеть, что происходит. Если этот макрос не определен, вы получаете тест буфера 'sort-of' (тот же текст без некоторых разделителей строк). Я не мог найти способ устроить более значимое столкновение потоков.

Полагаю, вы можете записывать в несколько файлов. Запись в один файл, вероятно, является лучшим тестом. Прикрепленная программа не является окончательным тестом. Хотя это может быть расширено, я не уверен, что какая-либо программа действительно может быть окончательной. Итог: может быть, вам стоит просто отключить вызовы fprintf ().

// artificial test for thread safety of fprintf()
// define FLUSH_BUFFER to get a good picture of what's happening, un-def for a buffer test
// the 'pretty print' (FLUSH_BUFFER) output relies on a mono-spaced font
// a writeable file name on the command line will send output to that file
//

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#define FLUSH_BUFFER

#define NTHREAD     5
#define ITERATIONS  3

const char DOTS[] = ". . . . . . . . . . . . . . . . . . . . . . . . . . . . ";
FILE *outFile;

void *PrintHello(void *threadid) {
    long tid;

    tid = (long)threadid;
    for (int i=1; i<=ITERATIONS; i++) {
        long delay = (NTHREAD-tid) * 100000 + (ITERATIONS-i+1) * 10000;
#ifdef FLUSH_BUFFER
        fprintf(outFile, "%*sStart thread  %d iteration %d\n", (tid+1)*4, " ", tid, i);
        usleep(delay);
        fprintf(outFile, "%*sFinish thread %d iteration %d %*.*sw/delay %d\n", 
               (tid+1)*4, " ", tid, i, (NTHREAD-tid+1)*4, (NTHREAD-tid+1)*4, DOTS, delay);
#else
        fprintf(outFile, "Start thread  %d iteration %d   ", tid, i);
        usleep(delay);
        fprintf(outFile, "Finish thread %d iteration %d w/delay %d\n", tid, i, delay);
#endif
    }
    pthread_exit(NULL);
}

int main (int argc, char *argv[]) {
    pthread_t threads[NTHREAD];
    char errStr[100];
    int rc;
    long t;

    if(argc > 1) {
        if(! (outFile = fopen(argv[1], "w"))) {
            perror(argv[1]);
            exit(1);
       }
    } else 
        outFile = stdout;

    for(t=0; t<NTHREAD; t++) {
        fprintf(outFile, "In main: creating thread %ld\n", t);
        if(rc = pthread_create(&threads[t], NULL, PrintHello, (void *)t)) {
            sprintf(errStr, "ERROR; pthread_create() returned %d", rc);
            perror(errStr);
            exit(2);
        }
    }
    pthread_exit(NULL);
}
...