我正在实现一个使用不同内核的多线程程序,并且同时执行许多线程。每个线程都进行printf()
调用,结果不可读。
如何使printf()
原子化,以便一个线程中的printf()
调用与另一个线程中的printf()
调用不冲突?
答案 0 :(得分:19)
POSIX规范包括以下功能:
getc_unlocked()
getchar_unlocked()
putc_unlocked()
putchar_unlock()
分别命名为
getc()
,getchar()
,putc()
的函数putchar()
,getc_unlocked()
,getchar_unlocked()
和putc_unlocked()
的版本,并且应提供putchar_unlocked()
,它们在功能上等同于原始版本,但不要求它们以完全线程安全的方式实现。在受flockfile()
(或ftrylockfile()
)和funlockfile()
保护的范围内使用时,它们应是线程安全的。当且仅当在调用线程拥有(FILE *
)对象时调用它们时,可以安全地在多线程程序中使用这些函数,就像成功调用flockfile()
或ftrylockfile()
函数。
这些功能的规范提到:
flockfile()
ftrylockfile()
funlockfile()
flockfile()
等的规范包括一揽子要求:
所有引用(
FILE *
)对象的函数,除了名称以_unlocked
结尾的对象外,其行为应该像在内部使用flockfile()
和funlockfile()
一样获得所有权这些(FILE *
)个对象。
这取代了此答案以前版本中的建议代码。 POSIX标准还规定:
[
*lockfile()
]函数的行为就像存在与每个(FILE *
)对象关联的锁定计数一样。创建(FILE *
)对象时,此计数隐式初始化为零。当计数为零时,(FILE *
)对象被解锁。当计数为正时,单个线程拥有(FILE *
)对象。调用flockfile()
函数时,如果计数为零或计数为正且调用者拥有(FILE *
)对象,则计数应递增。否则,调用线程将被挂起,等待计数返回零。每次调用funlockfile()
都会减少计数。这样就可以将与flockfile()
(或成功调用ftrylockfile()
)和funlockfile()
的来电匹配。
还有字符I / O功能的规范:
格式化输出功能在此处记录:
printf()
规范中的一个关键条款是:
fprintf()
和printf()
生成的字符将被打印,就像调用了fputc()
一样。
请注意使用',就像'一样。但是,每个printf()
函数都需要应用锁,以便在多线程应用程序中控制对流的访问。一次只有一个线程可以使用给定的文件流。如果操作是对fputc()
的用户级调用,则其他线程可以散布输出。如果操作是用户级调用,例如printf()
,则整个调用和对文件流的所有访问都得到有效保护,因此只有一个线程正在使用它,直到调用printf()
为止。< / p>
在关于Threads主题的POSIX的系统界面:一般信息部分中,它说:
2.9.1线程安全
本卷POSIX.1-2008定义的所有函数都是线程安全的,但以下函数1不需要是线程安全的。
...一系列不需要线程安全的函数...
...除非调用线程拥有(
getc_unlocked()
)对象,否则getchar_unlocked()
,putc_unlocked()
,putchar_unlocked()
和FILE *
函数不一定是线程安全的通过电话访问,就像成功调用flockfile()
或ftrylockfile()
函数后的情况一样。实现应根据需要提供内部同步,以满足此要求。
豁免功能列表不包含fputc
或putc
或putchar
(或printf()
等)。
改写2017-07-26。
printf()
之类的高级函数在结尾处从概念上调用flockfile()
funlockfile()
,这意味着POSIX定义的流输出函数也是线程安全的调用flockfile()
和funlockfile()
的调用来实现此操作(不会干扰系统的使用*lockfile()
函数。这意味着您无需为自己创建互斥锁或等效机制;该实现提供的功能允许您在多线程应用程序中控制对printf()
等的访问。
...之前回答的代码已删除,因为不再相关......
答案 1 :(得分:15)
为了不混合来自不同线程的输出,您需要确保一次只有一个线程使用printf
。要实现这一点,最简单的解决方案是使用mutex
。在开始时初始化mutex
:
static pthread_mutex_t printf_mutex;
...
int main()
{
...
pthread_mutex_init(&printf_mutex, NULL);
...
然后围绕printf
创建一个包装器,以确保只有获得mutex
的线程可以调用printf
(否则它必须阻塞,直到mutex
为可用):
int sync_printf(const char *format, ...)
{
va_list args;
va_start(args, format);
pthread_mutex_lock(&printf_mutex);
vprintf(format, args);
pthread_mutex_unlock(&printf_mutex);
va_end(args);
}
答案 2 :(得分:-2)
对于linux,这里是你在c:3个线程中的代码,在不同的核心上执行打印hello world,而不是相互冲突的锁定。
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <syscall.h>
#include <sys/types.h>
void * printA ( void *);
void * printB ( void *);
void * printC ( void *);
pthread_mutex_t mylock = PTHREAD_MUTEX_INITIALIZER;
int main(int argc, char *argv[]) {
int error;
pthread_t tid1, tid2,tid3;
if ( error = pthread_create (&tid1, NULL, printA, NULL ))
{
fprintf (stderr, "Failed to create first thread: %s\n",strerror(error));
return 1;
}
if ( error = pthread_create (&tid2, NULL, printB, NULL ))
{
fprintf (stderr, "Failed to create second thread: %s\n",strerror(error));
return 1;
}
if ( error = pthread_create (&tid3, NULL, printC, NULL ))
{
fprintf (stderr, "Failed to create second thread: %s\n",strerror(error));
return 1;
}
if (error = pthread_join(tid1, NULL))
{
fprintf (stderr, "Failed to join first thread: %s\n",strerror(error));
return 1;
}
if (error = pthread_join(tid2, NULL))
{
fprintf (stderr, "Failed to join second thread: %s\n",strerror(error));
return 1;
}
if (error = pthread_join(tid3, NULL))
{
fprintf (stderr, "Failed to join second thread: %s\n",strerror(error));
return 1;
}
return 0;
}
void * printA ( void *arg )
{
if ( error = pthread_mutex_lock( &mylock ))
{
fprintf (stderr, "Failed to acquire lock in printA: %s\n",strerror(error));
return NULL;
}
printf("Hello world\n");
if ( error = pthread_mutex_unlock( &mylock ))
{
fprintf (stderr, "Failed to release lock in printA: %s\n",strerror(error));
return NULL;
}
}
void * printB ( void *arg )
{
int error;
if ( error = pthread_mutex_lock( &mylock ))
{
fprintf (stderr, "Failed to acquire lock in printB: %s\n",strerror(error));
return NULL;
}
printf("Hello world\n");
if ( error = pthread_mutex_unlock( &mylock ))
{
fprintf (stderr, "Failed to release lock in printA: %s\n",strerror(error));
return NULL;
}
}
void * printC ( void *arg )
{
int error;
if ( error = pthread_mutex_lock( &mylock ))
{
fprintf (stderr, "Failed to acquire lock in printB: %s\n",strerror(error));
return NULL;
}
printf("Hello world\n");
if ( error = pthread_mutex_unlock( &mylock ))
{
fprintf (stderr, "Failed to release lock in printA: %s\n",strerror(error));
return NULL;
}
}