2013-05-13 5 views
0

Является ли функция pthreads функцией PrintHello() потокобезопасной? Я нахожу эти примеры в Интернете, но я не понимаю, как они могут быть потокобезопасными. С другой стороны, если я добавлю мьютекс вокруг кода в функции PrintHello(), тогда пример не будет многопоточным, поскольку все потоки будут ждать в очереди, пока предыдущий поток не завершит функцию PrintHello(). Кроме того, перемещение его в класс не помогло бы, так как члену было бы статически объявлено, что указатели на нестатические функции не допускаются с CreateThread(). Любой способ решить это?Thread-safe CreateThread?

#include <WinBase.h> 
#include <stdio.h> 
#include <stdlib.h>  /* srand, rand */ 
#include <time.h>  /* time */ 

#define NUM_THREADS  500 

DWORD PrintHello(LPVOID oHdlRequest) 
{ 
    long tid; 
    tid = (long)GetCurrentThreadId(); 

    /* randomly sleep between 1 and 10 seconds */ 
    int sleepTime = rand() % 10 + 1; 
    sleep(sleepTime); 

    printf("Hello World! It's me, thread #%ld!\n", tid); 
    return 0; 
} 

int main (int argc, char *argv[]) 
{ 
    /* initialize random seed: */ 
    srand (time(NULL)); 

    HANDLE threads[NUM_THREADS]; 
    long t; 
    DWORD nThreadID; 

    for(t=0; t<NUM_THREADS; t++){ 
     printf("In main: creating thread %ld\n", t); 

     threads[t] = CreateThread(
      // Default security 
      NULL, 
      // Default stack size 
      0, 
      // Function to execute 
      (LPTHREAD_START_ROUTINE)&PrintHello, 
      // Thread argument 
      NULL, 
      // Start the new thread immediately 
      0, 
      // Thread Id 
      &nThreadID 
     );  

     if (!threads[t]){ 
     printf("ERROR; return code from CreateThread() is %d\n", GetLastError()); 
     exit(-1); 
     } 
    } 
} 
+0

Что вы подразумеваете под "thread safe". Безопасно от * what *? – dlev

+0

@dlev http://en.wikipedia.org/wiki/Thread_safety «Часть кода является потокобезопасной, если она управляет только совместно используемыми структурами данных таким образом, чтобы гарантировать безопасное выполнение несколькими потоками одновременно». В простом примере выше это повлечет за собой, что 'printf' может повторить неправильный идентификатор потока. – 2013-05-13 07:49:14

+0

В этом определении говорится, что безопасность потоков - это выполнение кода, которое является «безопасным». Это не очень полезно :). Несмотря на это, я хочу сказать, что трудно определить, какие безопасные средства для потоков: никаких сбоев? Согласованные данные для абонентов? Обновленные данные? В этом случае код, похоже, не управляет общим состоянием, поэтому, как явствует из вышеприведенного определения, он тривиально «потокобезопасен». Тем не менее, я рекомендую вам прочитать эту статью: http://blogs.msdn.com/b/ericlippert/archive/2009/10/19/what-is-this-thing-you-call-thread-safe.aspx – dlev

ответ

6

Поскольку вы включили WinBase.h, я буду считать, что вы используете MSVC. MSVC CRT давно поддерживает многопоточный доступ. Фактически, текущие версии MSVC больше не поддерживают однострочный CRT, который не является потокобезопасным. Я считаю, что VS 2003 - это последняя версия MSVC, которая поддерживает однопоточный CRT.

В многопоточном CRT функции являются потокобезопасными, и если они получают глобальные данные, они синхронизируются между собой. Поэтому каждый printf(), выполненный в ProcessRequest(), будет атомарным по отношению к другим printf() вызовам в других потоках (на самом деле блокировки основаны на потоках, поэтому вызовы printf() будут атомарными по отношению к другим функциям CRT, которые используют stdout).

Исключения из этого правила - если вы используете функции ввода/вывода, которые явно задокументированы, чтобы не брать блокировки (чтобы вы могли синхронизировать их самостоятельно по соображениям производительности), или если вы определяете _CRT_DISABLE_PERFCRIT_LOCKS, в этом случае ЭЛТ предполагает, что все I/O будет выполняться в одном потоке.

См http://msdn.microsoft.com/en-us/library/ms235505.aspx

POSIX делает подобные гарантии, что printf() будет поточно:

  • http://pubs.opengroup.org/onlinepubs/9699919799/functions/flockfile.html

    Все функции, которые ссылаются (FILE *) объекты, за исключением тех имен, заканчивающихся _unlocked, должны вести себя так, как если бы они использовали flockfile() и funlockfile() внутри, чтобы получить право собственности на эти объекты (FILE *).

  • http://newsgroups.derkeiler.com/Archive/Comp/comp.programming.threads/2009-06/msg00058.html (Пост Дэвид Butenhof):

    POSIX/UNIX требует, чтобы Е() сама по себе быть атомарным; это не является законным, что два параллельных вызова printf() из отдельных потоков могут смешивать их данные. Но эти две записи могут отображаться на выходе в любом порядке.

+0

Если блокировки основаны на потоках, то 'printf ("% d% d ", x, y)' не является полностью потокобезопасным, поскольку он блокирует поток два раза. – Dialecticus

+0

@Dialecticus: Зачем он блокирует поток два раза? 'printf()' блокирует поток, выполняет ввод-вывод, затем разблокирует поток. –

+0

О, мой плохой. Я думал, что 'printf' вызывает функцию другого, более низкого уровня, которая фактически блокирует поток. – Dialecticus

0

Код не является потокобезопасным в целом; printf обычно реенторант. (Реализация может добавить к нему еще одну добавочную функцию: , но я не знаю, кто это делает.) Вы должны добавить к ней какую-то защиту. (Под Windows, должно быть достаточно CriticalSection.)

Вам также необходимо найти альтернативу потоку, безопасную для sleep; Я не могу найти документацию, в которой говорится, что она является реентерабельной (и вариант Posix не является), но Microsoft, похоже, не документация в целом. Классическим решением для этого было бы создать Mutex, заблокировать его, а затем вызвать WaitForSingleObject на нем с желаемым таймаутом; CreateWaitableTimer и WaitForSingleObject должны работать как хорошо. (Как я уже сказал, документация Microsoft является очень дефицитной, но WaitForSingleObject должен быть безопасным, так как де igned в использоваться при ожидании взаимной блокировки, между прочим.)

Примечания также, что если вы не присоединиться к созданной потоки, вы должны , вероятно, закончите с конца main, и процесс завершится до того, как какой-либо поток будет запущен. (Под Windows, вы можете использовать WaitForSingleObject или WaitForMultipleObjects присоединиться.)

Даже лучшим решением было бы стандартных резьб, если есть компилятор, который поддерживает их, или увеличить threds, если вы Дон» т.

Смежные вопросы