2016-09-10 5 views
1

Рассмотрим следующий фрагмент кода, работающего под Solaris 11.3:Multithreaded Printf против записи (2) буферизацию

void *run(void *args) { 
    int i; 
    for (i = 0; i < 100; i++) { 
    printf("Line #%d printed by child\n", i + 1); 
    } 
} 

int main() { 
    pthread_t tid; 
    int ret = pthread_create(&tid, NULL, run, NULL); 
    int i; 
    for (i = 0; i < 100; i++) { 
    printf("Line #%d printed by parent\n", i + 1); 
    } 
    pthread_exit(NULL); 
} 

После запуска, родительский поток всегда выводит все свои линии перед вновь созданной нити. Некоторые могут сказать, что pthread_create слишком медленно, чтобы другой поток даже начал свою работу до того, как возвращается основной поток. Однако замена printf на write(2) дает ожидаемый выход, где потоки иногда чередуются с печатью своих линий.

Как можно объяснить это поведение?

Буферизация, безусловно, не имеет ничего общего с ним, поскольку вывод терминала по умолчанию буферизирован по строке; это должно быть справедливо как для write, так и для printf.

Даже если функции используют какую-то блокирующую технику для обеспечения безопасности потоков, что именно предотвратило бы выход из скремблирования?

+6

Это _is_ проблема буферизации. Вы путаете 'write (2)' с 'fwrite (3)'. 'write (2)' является низкоуровневой функцией и не загружается. 'fwrite (3)' является 'stdio' функцией и буферизуется по умолчанию. Вы получаете этот эффект, потому что 'stdout' получает блок-буферизацию в фоновом потоке, в то время как он остается буферизированным в строке в родительском. Добавьте 'setbuf (stdout, NULL)' в оба потока и сравните. –

+0

Выход другой нити по-прежнему идет на терминал, так что не следует ли это буферизовать по строке, как обычно? – ilkkachu

+4

«Буферизация строк» ​​является аспектом 'stdio', а не« terminal ». 'write (2)' не использует 'stdio' и поэтому не буферизуется. –

ответ

1

Это по причине скорости. Если вы поместите вызов usleep между pthread_create и циклом и немного настройте значение, вы можете заставить его смешать некоторые строки.

Обратите внимание, что из-за блокировки stdio, когда один поток не может получить блокировку по stdout один или два раза, он может спать в течение нескольких миллисекунд (что позволит другому потоку завершить цикл), так что вы выиграли Я вижу много смешивания. Если вы хотите больше микширования, вам нужно также поставить usleep в петлю.

На моей машине usleep(50) перед циклом в родительском и usleep(1) в обеих циклах приведет к почти чередующимся линиям.

+0

'sched_yield' на моей машине также позволяет череду чередовать каждый раз , и это именно то, что я ищу. Спасибо! –