6.087 Practical Programming in C, lec13

Multithreaded programming. Socketsand asynchronous I/OMultithreaded programming• OS implements scheduler – determines which threads execute • Scheduling may execute threads in arbitrary order
摘要由CSDN通过智能技术生成

Multithreaded programming. Socketsand asynchronous I/O

Multithreaded programming

• OS implements scheduler – determines which threads execute

• Scheduling may execute threads in arbitrary order

• Without proper synchronization, code can execute non-deterministically

• Suppose we have two threads: 1 reads a variable, 2 modifies that variable

• Scheduler may execute 1, then 2, or 2 then 1

• Non-determinism creates a race condition – where the behavior/result depends on the order of execution

线程是由操作系统来控制的,而不是程序自身。尽管进程也是由操作系统控制,在执行的时候会中断,但因为进程有自己的内存空间,因此一般不会发生线程中共享资源访问的问题。Racecondition是指结果受执行顺序影响的情况。

Race conditions in assembly

Consider the following program race.c:

unsigned int cnt =0;

void ∗count (void ∗arg){/∗thread body∗/

int i;

for ( i = 0 ; i < 100000000; i ++)

cnt++;

return NULL;

}

int main(void){

pthread_t tids[4];

int i;

for (i = 0; i < 4; i++)

pthread_create(&tids[i], NULL, count, NULL);

for(i = 0; i < 4; i++)

pthread_join(tids[i], NULL);

printf("cnt=%u\n", cnt);

return 0;

}

What is the value of cnt?

[Bryant and O’Halloran. Computer Systems: A Programmer’sPerspective. Prentice Hall, 2003.]

Ideally, should increment cnt 4 × 100000000 times, so cnt =400000000. However, running our code gives:

athena% ./race.o

cnt=137131900

athena% ./race.o

cnt=163688698

athena% ./race.o

cnt=169695163

So, what happened?

• C not designed for multithreading

• No notion of atomic operations in C

• Increment cnt++; maps to three assembly operations:

1. load cnt into a register

2. increment value in register

3. save new register value as new cnt

• So what happens if thread interrupted in the middle?

• Race condition!

上述程序结果令人吃惊,一个重要原因是我本以为++是原子操作,现在发现++并不是原子操作,看来要真正理解程序,还是要学编译原理啊。如何解决这个问题?很简单,加锁使其成为原子操作皆可。加锁看起来是一种简便的方式,随程序的复杂,锁也带来了很多问题,尤其是手动来控制加锁、解锁的过程,更容易出错。上面也提到,问题的一个重要根源就是C在设计时并没有考虑多线程的问题。而Java等变成语言提供了synchronized关键字等设施来自动地解决这个问题,大大减轻了程序员的负担。

Let’s fix our code:

pthread_mutex_t mutex;

unsigned int cnt = 0;

void ∗count (void ∗arg){/∗thread body∗/

int i;

for(i = 0; i < 100000000; i++){

pthread_mutex_lock(&mutex);

cnt++;

pthread_mutex_unlock (&mutex ) ;

}

return NULL;

}

int main ( void ) {

pthread_t tids[4];

int i;

pthread_mutex_init(&mutex, NULL);

for (i = 0; i < 4; i++)

pthread_create(&tids[i], NULL, count, NULL);

for(i = 0; i < 4; i++)

pthread_join(tids[i], NULL);

pthread_mutex_dest roy (&mutex ) ;

printf("cnt=%u\n", cnt);

return 0;

}

如上面的代码所示,对读写公共数据的操作加锁,即可解决这个问题。当然,加解锁自身必须是原子操作,否则加解锁的过程也是racecondition

Race conditions

• Note that new code func

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值