vsprintf是线程安全的吗?

assembly 2014-05-07 01:58:39
编译器1:VC6
编译器2:IAR for ARM 6.5

这个问题的另一个问法是:vsprintf有没有使用 全局变量?

VC6下有一份源码,看的云里雾里;IAR里没有源码。

如果要找一份有参考价值的代码,能否推荐一下?libc ? glibc?

有经验的大大请不吝赐教,谢谢!!
...全文
960 30 打赏 收藏 转发到动态 举报
写回复
用AI写文章
30 条回复
切换为时间正序
请发表友善的回复…
发表回复
whoho 2014-08-09
  • 打赏
  • 举报
回复
引用 26 楼 assembly 的回复:
[quote=引用 25 楼 zhao4zhong1 的回复:] 谁说不操作全局变量的函数就是线程安全的?!
我一直是这么认为的,难道不对? 赵老师,能不能给个反例?谢谢了![/quote] 倒也不能认为没引用全局变量就线程安全 如果你给两个线程两个指针指向同一个栈上分配的内存对象,不加锁仍然是线程不安全 又或者使用同一个资源,比如文件或者套接字 但是这里的vsprintf,恐怕在两个线程中撞车的可能性不大吧,如果不对同一个内存缓冲区操作的话 我想象不出一个现代的库实现,vsprintf内部还有什么必要共享某种资源,无论内存 还是文件、套接字等等等等 至于说vsprintf内部加锁,大家打个赌,谁找到有这样的实现就赠送个100分啥的,呵呵——不过不是我给哦
码工许师傅 2014-07-26
  • 打赏
  • 举报
回复
引用 24 楼 assembly 的回复:
什么论坛啊,竟然不能修改自己的帖子! 抱歉,我收回楼上的测试;因为发现了一个问题: xusiwei1236: 你好!你的测试用例有问题,因为两个线程使用了相同的 char buf[1024],因此而产生的字符混合,不能怪printf。 我修改了你的代码,把 char buf[1024] 分别挪到两个线程各自的函数内部,测试的结果是:没有混合 不妨你也测试一下。
我只是要证明“sprintf内没有加锁‘这一观点,因而使用了两个线程同时操作同一个str。如果两个线程同时用sprintf格式化两个不同的str,不会有任何问题。 如果sprintf内部加了锁,那么多个线程各自操作不同的字符串(刚才打错了)也会发生锁争用,而这种争用是没有必要的。
码工许师傅 2014-07-26
  • 打赏
  • 举报
回复
引用 24 楼 assembly 的回复:
什么论坛啊,竟然不能修改自己的帖子! 抱歉,我收回楼上的测试;因为发现了一个问题: xusiwei1236: 你好!你的测试用例有问题,因为两个线程使用了相同的 char buf[1024],因此而产生的字符混合,不能怪printf。 我修改了你的代码,把 char buf[1024] 分别挪到两个线程各自的函数内部,测试的结果是:没有混合 不妨你也测试一下。
我只是要证明“sprintf内没有加锁‘这一观点,因而使用了两个线程同时操作同一个str。如果两个线程同时用sprintf格式化两个不同的str,不会有任何问题。 如果sprintf内部加了锁,那么多个线程对各自操作不同的线程也会发生锁争用,而这种争用是没有必要的。
assembly 2014-05-23
  • 打赏
  • 举报
回复
引用 25 楼 zhao4zhong1 的回复:
谁说不操作全局变量的函数就是线程安全的?!
我一直是这么认为的,难道不对? 赵老师,能不能给个反例?谢谢了!
赵4老师 2014-05-19
  • 打赏
  • 举报
回复
谁说不操作全局变量的函数就是线程安全的?!
assembly 2014-05-09
  • 打赏
  • 举报
回复
什么论坛啊,竟然不能修改自己的帖子! 抱歉,我收回楼上的测试;因为发现了一个问题: xusiwei1236: 你好!你的测试用例有问题,因为两个线程使用了相同的 char buf[1024],因此而产生的字符混合,不能怪printf。 我修改了你的代码,把 char buf[1024] 分别挪到两个线程各自的函数内部,测试的结果是:没有混合 不妨你也测试一下。
assembly 2014-05-09
  • 打赏
  • 举报
回复
xusiwei1236: 你好! 我在Windows XP service pack3 + VC6的环境下,跑了你的代码,不过有一点点调整。测试的结果是这样的: 1.用CriticalSection把两个语句都包括进来,测试了几次,字符是干净的,没有发生混合

void thread_fun1(void *args) {
    int i;
    for(i=0; i<TESTN; i++) {
        EnterCriticalSection (&critical_section);
        sprintf(buf, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", 
                "#","#","#","#","#","#","#","#","#","#",
                "#","#","#","#","#","#","#","#","#","#",
                "#","#","#","#","#","#","#","#","#","#",
                "#","#","#","#","#","#","#","#","#","#",
                "#","#","#","#","#","#","#","#","#","#",
                "#","#","#","#","#","#","#","#","#","#",
                "#","#","#","#","#","#","#","#","#","#",
                "#","#","#","#","#","#","#","#","#","#"
                );
        printf("[thread 1] %03d | %s\n", i, buf); // printf 一般是线程安全的
        LeaveCriticalSection (&critical_section);
    }
}
2.只用CriticalSection保护sprintf,结果:字符发生了混合;这说明:即使printf是安全的,但它保护的并不是整个字符串;猜测也许是一段一段保护的,当然只是猜测。

void thread_fun1(void *args) {
    int i;
    for(i=0; i<TESTN; i++) {
        EnterCriticalSection (&critical_section);
        sprintf(buf, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", 
                "#","#","#","#","#","#","#","#","#","#",
                "#","#","#","#","#","#","#","#","#","#",
                "#","#","#","#","#","#","#","#","#","#",
                "#","#","#","#","#","#","#","#","#","#",
                "#","#","#","#","#","#","#","#","#","#",
                "#","#","#","#","#","#","#","#","#","#",
                "#","#","#","#","#","#","#","#","#","#",
                "#","#","#","#","#","#","#","#","#","#"
                );
        LeaveCriticalSection (&critical_section);
        printf("[thread 1] %03d | %s\n", i, buf); // printf 一般是线程安全的
    }
}
3.只用CriticalSection保护printf,结果:字符发生了混合;参考结果2,个人觉得,也许sprintf没保护;也许sprintf保护了,但不是整个字符串。

void thread_fun1(void *args) {
    int i;
    for(i=0; i<TESTN; i++) {
        sprintf(buf, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", 
                "#","#","#","#","#","#","#","#","#","#",
                "#","#","#","#","#","#","#","#","#","#",
                "#","#","#","#","#","#","#","#","#","#",
                "#","#","#","#","#","#","#","#","#","#",
                "#","#","#","#","#","#","#","#","#","#",
                "#","#","#","#","#","#","#","#","#","#",
                "#","#","#","#","#","#","#","#","#","#",
                "#","#","#","#","#","#","#","#","#","#"
                );
        EnterCriticalSection (&critical_section);
        printf("[thread 1] %03d | %s\n", i, buf); // printf 一般是线程安全的
        LeaveCriticalSection (&critical_section);
    }
}
4.不使用CriticalSection,当然发生了混合。
赵4老师 2014-05-08
  • 打赏
  • 举报
回复
VC6: C:\Program Files\Microsoft Visual Studio\VC98\CRT\SRC\VSPRINTF.C
赵4老师 2014-05-08
  • 打赏
  • 举报
回复
引用 11 楼 zhao4zhong1 的回复:
为何不去看看vsprintf的源代码?
Windows: 先 http://www.microsoft.com/visualstudio/chs/downloads#d-2010-express 点开Visual C++ 2010 Express下面的语言选‘简体中文’,再点立即安装 在参考C:\Program Files\Microsoft Visual Studio 10.0\VC\crt\src\vsprintf.c Linux: 略
赵4老师 2014-05-08
  • 打赏
  • 举报
回复
为何不去看看vsprintf的源代码?
码工许师傅 2014-05-08
  • 打赏
  • 举报
回复
正常的实现都不会使用全局变量(或static局部变量), 也就是说,对于不同的str,是线程安全的。 如果有两个线程同时操作同一个str,是不安全的。

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

#ifdef WIN32
	#include <windows.h>
	#define msleep(ms) Sleep((ms))
#else
	#include <unistd.h>
	#define msleep(ms) usleep((ms)*1000)
#endif

#define TESTN 10
#define DELAY 100

char buf[1024];
pthread_t tid1, tid2;

void *thread_fun1(void *args) {
	int i;
	for(i=0; i<TESTN; i++) {
		sprintf(buf, "%s%s%s%s%s%s%s%s%s%s", 
				"#","#","#","#","#","#","#","#","#","#" );
		printf("[thread 1] %03d | %s\n", i, buf); // printf 一般是线程安全的
	}
}

void *thread_fun2(void *args) {
	int i;
	for(i=0; i<TESTN; i++) {
		sprintf(buf, "%s%s%s%s%s%s%s%s%s%s", 
					 ".",".",".",".",".",".",".",".",".","." );	
		printf("[THREAD 2] %03d | %s\n", i, buf); // printf 一般是线程安全的
	}
}

int main(int argc, char *argv[])
{
	pthread_create(&tid1, NULL, thread_fun1, NULL);
	pthread_create(&tid2, NULL, thread_fun2, NULL);
	
	msleep(100);
	
	return 0;
}
MinGW编译,连续运行10次,重定位到spts.txt:

for /L %i in (1,1,10) do @echo test %i...  & @a >> spts.txt
得到spts.txt(同一行内出现两种字符的就是因为sprintf内没有加锁):

[thread 1] 000 | ##########
[thread 1] 001 | ##########
[thread 1] 002 | ##########
[thread 1] 003 | ##########
[thread 1] 004 | ##########
[thread 1] 005 | ##########
[thread 1] 006 | ##########
[thread 1] 007 | ##########
[thread 1] 008 | ##########
[thread 1] 009 | ##########
[THREAD 2] 000 | ..........
[THREAD 2] 001 | ..........
[THREAD 2] 002 | ..........
[THREAD 2] 003 | ..........
[THREAD 2] 004 | ..........
[THREAD 2] 005 | ..........
[THREAD 2] 006 | ..........
[THREAD 2] 007 | ..........
[THREAD 2] 008 | ..........
[THREAD 2] 009 | ..........
[thread 1] 000 | ##########
[thread 1] 001 | ##########
[thread 1] 002 | ##########
[thread 1] 003 | ##########
[thread 1] 004 | ##########
[thread 1] 005 | ##########
[thread 1] 006 | ##########
[thread 1] 007 | ##########
[thread 1] 008 | ##########
[thread 1] 009 | ##########
[THREAD 2] 000 | ..........
[THREAD 2] 001 | ..........
[THREAD 2] 002 | ..........
[THREAD 2] 003 | ..........
[THREAD 2] 004 | ..........
[THREAD 2] 005 | ..........
[THREAD 2] 006 | ..........
[THREAD 2] 007 | ..........
[THREAD 2] 008 | ..........
[THREAD 2] 009 | ..........
[thread 1] 000 | ##########
[thread 1] 001 | ##########
[thread 1] 002 | ##########
[thread 1] 003 | ##########
[thread 1] 004 | ##########
[thread 1] 005 | ##########
[thread 1] 006 | ##########
[thread 1] 007 | ##########
[thread 1] 008 | ##########
[thread 1] 009 | ##########
[THREAD 2] 000 | ..........
[THREAD 2] 001 | ..........
[THREAD 2] 002 | ..........
[THREAD 2] 003 | ..........
[THREAD 2] 004 | ..........
[THREAD 2] 005 | ..........
[THREAD 2] 006 | ..........
[THREAD 2] 007 | ..........
[THREAD 2] 008 | ..........
[THREAD 2] 009 | ..........
[thread 1] 000 | ##########
[thread 1] 001 | ##########
[thread 1] 002 | ##########
[thread 1] 003 | ...#######
[THREAD 2] 000 | ##########
[thread 1] 004 | ..........
[THREAD 2] 001 | ##########
[thread 1] 005 | ..........
[THREAD 2] 002 | ##########
[thread 1] 006 | ..........
[THREAD 2] 003 | ##########
[thread 1] 007 | ..........
[THREAD 2] 004 | ##########
[thread 1] 008 | ..........
[THREAD 2] 005 | ##########
[thread 1] 009 | ..........
[THREAD 2] 006 | ..........
[THREAD 2] 007 | ..........
[THREAD 2] 008 | ..........
[THREAD 2] 009 | ..........
[thread 1] 000 | ##########
[thread 1] 001 | ##########
[thread 1] 002 | ##########
[thread 1] 003 | ##########
[thread 1] 004 | ##########
[thread 1] 005 | ##########
[thread 1] 006 | ##########
[thread 1] 007 | ##########
[thread 1] 008 | ##########
[thread 1] 009 | ##########
[THREAD 2] 000 | ..........
[THREAD 2] 001 | ..........
[THREAD 2] 002 | ..........
[THREAD 2] 003 | ..........
[THREAD 2] 004 | ..........
[THREAD 2] 005 | ..........
[THREAD 2] 006 | ..........
[THREAD 2] 007 | ..........
[THREAD 2] 008 | ..........
[THREAD 2] 009 | ..........
[thread 1] 000 | ##########
[thread 1] 001 | ##########
[thread 1] 002 | ##########
[thread 1] 003 | ##########
[thread 1] 004 | ##########
[thread 1] 005 | ##########
[thread 1] 006 | ##########
[thread 1] 007 | ##########
[thread 1] 008 | ##########
[thread 1] 009 | ##########
[THREAD 2] 000 | ..........
[THREAD 2] 001 | ..........
[THREAD 2] 002 | ..........
[THREAD 2] 003 | ..........
[THREAD 2] 004 | ..........
[THREAD 2] 005 | ..........
[THREAD 2] 006 | ..........
[THREAD 2] 007 | ..........
[THREAD 2] 008 | ..........
[THREAD 2] 009 | ..........
[thread 1] 000 | ##########
[thread 1] 001 | ..........
[thread 1] 002 | ##########
[THREAD 2] 000 | ##########
[thread 1] 003 | ..........
[THREAD 2] 001 | ##########
[thread 1] 004 | ..........
[THREAD 2] 002 | ##########
[thread 1] 005 | ..........
[THREAD 2] 003 | ##########
[thread 1] 006 | ..........
[THREAD 2] 004 | ##########
[thread 1] 007 | ..........
[THREAD 2] 005 | ##########
[thread 1] 008 | ..........
[THREAD 2] 006 | ##########
[thread 1] 009 | ..........
[THREAD 2] 007 | ..........
[THREAD 2] 008 | ..........
[THREAD 2] 009 | ..........
[thread 1] 000 | ##########
[thread 1] 001 | ##########
[thread 1] 002 | ##########
[thread 1] 003 | ##########
[thread 1] 004 | ##########
[thread 1] 005 | ..........
[THREAD 2] 000 | ##########
[thread 1] 006 | ..........
[THREAD 2] 001 | ##########
[thread 1] 007 | ..........
[THREAD 2] 002 | ##########
[thread 1] 008 | ..........
[THREAD 2] 003 | ##########
[thread 1] 009 | ..........
[THREAD 2] 004 | ..........
[THREAD 2] 005 | ..........
[THREAD 2] 006 | ..........
[THREAD 2] 007 | ..........
[THREAD 2] 008 | ..........
[THREAD 2] 009 | ..........
[thread 1] 000 | ##########
[THREAD 2] 000 | ##########
[thread 1] 001 | ..........
[THREAD 2] 001 | ##########
[thread 1] 002 | ..........
[THREAD 2] 002 | ##########
[thread 1] 003 | ..........
[THREAD 2] 003 | ##########
[thread 1] 004 | ..........
[THREAD 2] 004 | ##########
[thread 1] 005 | ..........
[THREAD 2] 005 | ##########
[thread 1] 006 | ..........
[THREAD 2] 006 | ##########
[thread 1] 007 | ..........
[THREAD 2] 007 | ##########
[thread 1] 008 | ..........
[THREAD 2] 008 | ##########
[thread 1] 009 | ..........
[THREAD 2] 009 | ..........
[thread 1] 000 | ##########
[thread 1] 001 | #######...
[thread 1] 002 | ##########
[THREAD 2] 000 | ##########
[thread 1] 003 | ..........
[THREAD 2] 001 | ##########
[thread 1] 004 | ..........
[THREAD 2] 002 | ##########
[thread 1] 005 | ..........
[THREAD 2] 003 | ##########
[thread 1] 006 | ..........
[THREAD 2] 004 | ##########
[thread 1] 007 | ..........
[THREAD 2] 005 | ##########
[thread 1] 008 | ..........
[THREAD 2] 006 | ##########
[thread 1] 009 | ..........
[THREAD 2] 007 | ..........
[THREAD 2] 008 | ..........
[THREAD 2] 009 | ..........
ithiker 2014-05-08
  • 打赏
  • 举报
回复
引用 10 楼 xusiwei1236 的回复:
正常的实现都不会使用全局变量(或static局部变量), 也就是说,对于不同的str,是线程安全的。 如果有两个线程同时操作同一个str,是不安全的。

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

#ifdef WIN32
	#include <windows.h>
	#define msleep(ms) Sleep((ms))
#else
	#include <unistd.h>
	#define msleep(ms) usleep((ms)*1000)
#endif

#define TESTN 10
#define DELAY 100

char buf[1024];
pthread_t tid1, tid2;

void *thread_fun1(void *args) {
	int i;
	for(i=0; i<TESTN; i++) {
		sprintf(buf, "%s%s%s%s%s%s%s%s%s%s", 
				"#","#","#","#","#","#","#","#","#","#" );
		printf("[thread 1] %03d | %s\n", i, buf); // printf 一般是线程安全的
	}
}

void *thread_fun2(void *args) {
	int i;
	for(i=0; i<TESTN; i++) {
		sprintf(buf, "%s%s%s%s%s%s%s%s%s%s", 
					 ".",".",".",".",".",".",".",".",".","." );	
		printf("[THREAD 2] %03d | %s\n", i, buf); // printf 一般是线程安全的
	}
}

int main(int argc, char *argv[])
{
	pthread_create(&tid1, NULL, thread_fun1, NULL);
	pthread_create(&tid2, NULL, thread_fun2, NULL);
	
	msleep(100);
	
	return 0;
}
MinGW编译,连续运行10次,重定位到spts.txt:

for /L %i in (1,1,10) do @echo test %i...  & @a >> spts.txt
得到spts.txt(同一行内出现两种字符的就是因为sprintf内没有加锁):

[thread 1] 000 | ##########
[thread 1] 001 | ##########
[thread 1] 002 | ##########
[thread 1] 003 | ##########
[thread 1] 004 | ##########
[thread 1] 005 | ##########
[thread 1] 006 | ##########
[thread 1] 007 | ##########
[thread 1] 008 | ##########
[thread 1] 009 | ##########
[THREAD 2] 000 | ..........
[THREAD 2] 001 | ..........
[THREAD 2] 002 | ..........
[THREAD 2] 003 | ..........
[THREAD 2] 004 | ..........
[THREAD 2] 005 | ..........
[THREAD 2] 006 | ..........
[THREAD 2] 007 | ..........
[THREAD 2] 008 | ..........
[THREAD 2] 009 | ..........
[thread 1] 000 | ##########
[thread 1] 001 | ##########
[thread 1] 002 | ##########
[thread 1] 003 | ##########
[thread 1] 004 | ##########
[thread 1] 005 | ##########
[thread 1] 006 | ##########
[thread 1] 007 | ##########
[thread 1] 008 | ##########
[thread 1] 009 | ##########
[THREAD 2] 000 | ..........
[THREAD 2] 001 | ..........
[THREAD 2] 002 | ..........
[THREAD 2] 003 | ..........
[THREAD 2] 004 | ..........
[THREAD 2] 005 | ..........
[THREAD 2] 006 | ..........
[THREAD 2] 007 | ..........
[THREAD 2] 008 | ..........
[THREAD 2] 009 | ..........
[thread 1] 000 | ##########
[thread 1] 001 | ##########
[thread 1] 002 | ##########
[thread 1] 003 | ##########
[thread 1] 004 | ##########
[thread 1] 005 | ##########
[thread 1] 006 | ##########
[thread 1] 007 | ##########
[thread 1] 008 | ##########
[thread 1] 009 | ##########
[THREAD 2] 000 | ..........
[THREAD 2] 001 | ..........
[THREAD 2] 002 | ..........
[THREAD 2] 003 | ..........
[THREAD 2] 004 | ..........
[THREAD 2] 005 | ..........
[THREAD 2] 006 | ..........
[THREAD 2] 007 | ..........
[THREAD 2] 008 | ..........
[THREAD 2] 009 | ..........
[thread 1] 000 | ##########
[thread 1] 001 | ##########
[thread 1] 002 | ##########
[thread 1] 003 | ...#######
[THREAD 2] 000 | ##########
[thread 1] 004 | ..........
[THREAD 2] 001 | ##########
[thread 1] 005 | ..........
[THREAD 2] 002 | ##########
[thread 1] 006 | ..........
[THREAD 2] 003 | ##########
[thread 1] 007 | ..........
[THREAD 2] 004 | ##########
[thread 1] 008 | ..........
[THREAD 2] 005 | ##########
[thread 1] 009 | ..........
[THREAD 2] 006 | ..........
[THREAD 2] 007 | ..........
[THREAD 2] 008 | ..........
[THREAD 2] 009 | ..........
[thread 1] 000 | ##########
[thread 1] 001 | ##########
[thread 1] 002 | ##########
[thread 1] 003 | ##########
[thread 1] 004 | ##########
[thread 1] 005 | ##########
[thread 1] 006 | ##########
[thread 1] 007 | ##########
[thread 1] 008 | ##########
[thread 1] 009 | ##########
[THREAD 2] 000 | ..........
[THREAD 2] 001 | ..........
[THREAD 2] 002 | ..........
[THREAD 2] 003 | ..........
[THREAD 2] 004 | ..........
[THREAD 2] 005 | ..........
[THREAD 2] 006 | ..........
[THREAD 2] 007 | ..........
[THREAD 2] 008 | ..........
[THREAD 2] 009 | ..........
[thread 1] 000 | ##########
[thread 1] 001 | ##########
[thread 1] 002 | ##########
[thread 1] 003 | ##########
[thread 1] 004 | ##########
[thread 1] 005 | ##########
[thread 1] 006 | ##########
[thread 1] 007 | ##########
[thread 1] 008 | ##########
[thread 1] 009 | ##########
[THREAD 2] 000 | ..........
[THREAD 2] 001 | ..........
[THREAD 2] 002 | ..........
[THREAD 2] 003 | ..........
[THREAD 2] 004 | ..........
[THREAD 2] 005 | ..........
[THREAD 2] 006 | ..........
[THREAD 2] 007 | ..........
[THREAD 2] 008 | ..........
[THREAD 2] 009 | ..........
[thread 1] 000 | ##########
[thread 1] 001 | ..........
[thread 1] 002 | ##########
[THREAD 2] 000 | ##########
[thread 1] 003 | ..........
[THREAD 2] 001 | ##########
[thread 1] 004 | ..........
[THREAD 2] 002 | ##########
[thread 1] 005 | ..........
[THREAD 2] 003 | ##########
[thread 1] 006 | ..........
[THREAD 2] 004 | ##########
[thread 1] 007 | ..........
[THREAD 2] 005 | ##########
[thread 1] 008 | ..........
[THREAD 2] 006 | ##########
[thread 1] 009 | ..........
[THREAD 2] 007 | ..........
[THREAD 2] 008 | ..........
[THREAD 2] 009 | ..........
[thread 1] 000 | ##########
[thread 1] 001 | ##########
[thread 1] 002 | ##########
[thread 1] 003 | ##########
[thread 1] 004 | ##########
[thread 1] 005 | ..........
[THREAD 2] 000 | ##########
[thread 1] 006 | ..........
[THREAD 2] 001 | ##########
[thread 1] 007 | ..........
[THREAD 2] 002 | ##########
[thread 1] 008 | ..........
[THREAD 2] 003 | ##########
[thread 1] 009 | ..........
[THREAD 2] 004 | ..........
[THREAD 2] 005 | ..........
[THREAD 2] 006 | ..........
[THREAD 2] 007 | ..........
[THREAD 2] 008 | ..........
[THREAD 2] 009 | ..........
[thread 1] 000 | ##########
[THREAD 2] 000 | ##########
[thread 1] 001 | ..........
[THREAD 2] 001 | ##########
[thread 1] 002 | ..........
[THREAD 2] 002 | ##########
[thread 1] 003 | ..........
[THREAD 2] 003 | ##########
[thread 1] 004 | ..........
[THREAD 2] 004 | ##########
[thread 1] 005 | ..........
[THREAD 2] 005 | ##########
[thread 1] 006 | ..........
[THREAD 2] 006 | ##########
[thread 1] 007 | ..........
[THREAD 2] 007 | ##########
[thread 1] 008 | ..........
[THREAD 2] 008 | ##########
[thread 1] 009 | ..........
[THREAD 2] 009 | ..........
[thread 1] 000 | ##########
[thread 1] 001 | #######...
[thread 1] 002 | ##########
[THREAD 2] 000 | ##########
[thread 1] 003 | ..........
[THREAD 2] 001 | ##########
[thread 1] 004 | ..........
[THREAD 2] 002 | ##########
[thread 1] 005 | ..........
[THREAD 2] 003 | ##########
[thread 1] 006 | ..........
[THREAD 2] 004 | ##########
[thread 1] 007 | ..........
[THREAD 2] 005 | ##########
[thread 1] 008 | ..........
[THREAD 2] 006 | ##########
[thread 1] 009 | ..........
[THREAD 2] 007 | ..........
[THREAD 2] 008 | ..........
[THREAD 2] 009 | ..........
顶分析!
assembly 2014-05-08
  • 打赏
  • 举报
回复
mujiok2003的图片启发了我,去找了一下,发现一个问题:你的图片是IAR 5.4版本中所附文档《EWARM_DevelopmentGuide.ENU.pdf》中第290页上的一段文字;
然后我找了IAR 6.5版本中,相同的文档《EWARM_DevelopmentGuide.ENU.pdf》中叙述REENTRANCY的章节,这次是在第390页上,而且,内容也发生了有趣的变化:


而这也意味着,某些代码,在IAR 5.4上没有问题,而用IAR 6.5却有问题。

这些 没有明确规范 而必须依赖编译器和库具体实现 的细节,在 多线程 或者 中断 的应用环境下,有必要仔细考虑,甚至使用自己的实现。
mujiok2003 2014-05-08
  • 打赏
  • 举报
回复


你可以先看看编译器的手册。 至少sprintf可以放心使用。

引用 15 楼 assembly 的回复:
VC6是有源码的,但IAR的没看到。更换开发环境和编译器的时候,总是要面对这个问题。

如akirya所言,vsprintf内部使用了errno,而且C的标准库并没有对线程安全做强制的要求,那么不同的编译器的C库,就存在着使用其它全局量的可能,尽管只是可能,但这也是件让人纠结的事情。

我自己在ARM上写了一个多线程调度的核,用IAR编译。这样就要面对这个问题:线程代码如何放心地使用编译器提供的C库,包括string.h math.h stdlib.h。在编译器厂家不提供C库源码的情况下,该如何确认这些C库中的函数“没有使用全局量”?如果使用这些库函数的时候都拿锁来保护,对于一个C开发者来说,这是不可理喻的。

除非,使用自己的代码。但有。。那些经过底层优化的代码,不是那么容易折腾的,所以还是使用编译器自带的要明智些。

现在我决定这样做:
1.相信 string.h math.h 里的函数是不操作全局量的。
2.其它的函数,需要的时候,用自己的代码,包括vsprintf;不用的函数,就不包括。
mujiok2003 2014-05-08
  • 打赏
  • 举报
回复
以前VC的CRT有单线程与多线程版之分,现在只有多线程版了, 线程安全应该可以保证。
assembly 2014-05-08
  • 打赏
  • 举报
回复
这个核是我自己写的,创建线程的函数都是自己的。不怕大家笑话,就贴几句在这里,可以看到,是非常简陋的: void k_thread_create_context (struct _thread *p, void*(*proc)(void*), void *para) { ul cpsr; int i; cpsr = get_cpsr (); cpsr &= ~0x80; //enable IRQ, keep sys mode p->ctx.cpsr = cpsr; p->ctx.pc = (ul)proc; for (i=0; i<CTX_REG_NUM; i++) { p->ctx.r[i] = 0; } p->ctx.r[0] = (ul)para; p->ctx.sp = (ul)(&(p->stack[USR_STACK_SIZE-1])); p->ctx.lr = 0; } int pthread_create (pthread_t *t, const pthread_attr_t *ta, void *(*proc)(void *), void *para) { struct _thread *p; if (0 == pthread_idle) { return -1; } enter_critical (); p = pthread_idle; pthread_idle = pthread_idle->pnext; *t = (pthread_t)p; p->pnext = 0; k_thread_create_context (p, proc, para); k_thread_appand_ready (p); exit_critical (); return 0; } _beginthread内部是alloc了内存,给每个线程开辟了C库所需全局量的存储区;而相应的,C库的入口代码也需要相应的调整,比如说引用errno,可能就要写成这样:pthis_thread->errno。 如果开发者使用编译器提供的C库,那么C库里的errno可能就是errno,而不是pthis_thread->errno,因为C库的作者不知道开发者会如何创建线程以及线程的数据结构。
  • 打赏
  • 举报
回复
引用 15 楼 assembly 的回复:
VC6是有源码的,但IAR的没看到。更换开发环境和编译器的时候,总是要面对这个问题。 如akirya所言,vsprintf内部使用了errno,而且C的标准库并没有对线程安全做强制的要求,那么不同的编译器的C库,就存在着使用其它全局量的可能,尽管只是可能,但这也是件让人纠结的事情。 我自己在ARM上写了一个多线程调度的核,用IAR编译。这样就要面对这个问题:线程代码如何放心地使用编译器提供的C库,包括string.h math.h stdlib.h。在编译器厂家不提供C库源码的情况下,该如何确认这些C库中的函数“没有使用全局量”?如果使用这些库函数的时候都拿锁来保护,对于一个C开发者来说,这是不可理喻的。 除非,使用自己的代码。但有。。那些经过底层优化的代码,不是那么容易折腾的,所以还是使用编译器自带的要明智些。 现在我决定这样做: 1.相信 string.h math.h 里的函数是不操作全局量的。 2.其它的函数,需要的时候,用自己的代码,包括vsprintf;不用的函数,就不包括。
看你的编译器提供怎样的创建线程函数,应该有文档说明。 像VC提供的_beginthreadex就为每个线程提供一个errno,就没有线程安全的问题。
assembly 2014-05-08
  • 打赏
  • 举报
回复
竟然编辑不了自己的帖子。补充一下: 1.相信string.h math.h里的某几个函数是不操作全局量的。例如:memcpy strcpy sin cos等
assembly 2014-05-08
  • 打赏
  • 举报
回复
VC6是有源码的,但IAR的没看到。更换开发环境和编译器的时候,总是要面对这个问题。 如akirya所言,vsprintf内部使用了errno,而且C的标准库并没有对线程安全做强制的要求,那么不同的编译器的C库,就存在着使用其它全局量的可能,尽管只是可能,但这也是件让人纠结的事情。 我自己在ARM上写了一个多线程调度的核,用IAR编译。这样就要面对这个问题:线程代码如何放心地使用编译器提供的C库,包括string.h math.h stdlib.h。在编译器厂家不提供C库源码的情况下,该如何确认这些C库中的函数“没有使用全局量”?如果使用这些库函数的时候都拿锁来保护,对于一个C开发者来说,这是不可理喻的。 除非,使用自己的代码。但有。。那些经过底层优化的代码,不是那么容易折腾的,所以还是使用编译器自带的要明智些。 现在我决定这样做: 1.相信 string.h math.h 里的函数是不操作全局量的。 2.其它的函数,需要的时候,用自己的代码,包括vsprintf;不用的函数,就不包括。
ForestDB 2014-05-08
  • 打赏
  • 举报
回复
个人愚见:不认为vsprintf的实现需要全局变量,LZ可以看看代码中有无static变量或者其它全局变量,一般如果实现中使用了static这样的变量或者是全角变量是会在文档中提及的;但显然vsprintf的文档没有说 所以如果传给它的buffer是两个不同的buffer,个人认为vsprintf是线程安全的。
加载更多回复(9)

69,371

社区成员

发帖
与我相关
我的任务
社区描述
C语言相关问题讨论
社区管理员
  • C语言
  • 花神庙码农
  • 架构师李肯
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧