30、深入理解计算机系统笔记,并发编程(concurrent)(2)

1、共享变量

1)线程存储模型

线程由内核自动调度,每个线程都有它自己的线程上下文(thread context),包括一个惟一的整数线程IDThread ID,TID),栈,栈指针,程序计数器,通用目的寄存器和条件码。每个线程和其他线程一起共享进程上下文的剩余部分,包括整个用户的虚拟地址空间,它是由只读文本(代码),读/写数据,堆以及所有的共享库代码和数据区域组成的,还有,线程也共享同样的打开文件的集合。[1]

寄存器从不共享,而虚拟存储器总是共享。

The memory model for the separate thread stacks is not as clean(整齐清楚的). These stacks are contained in the stack area of the virtual address space, and are usually accessed independently by their respective threads. We say usually rather than always, because different thread stacks are not protected from other threads. So if a thread somehow manages to acquire a pointer to another threads stack, then it can read and write any part of that stack. 示例29行中, where the peer threads reference the contents of the main threads stack indirectly through the global ptr variable.

2)将变量映射到存储器

    和全局变量一样,虚拟存储器的/写区域只包含在程序中声明的每个本地静态变量的一个实例。每个线程的栈都包含它自己的所有本地自动变量的实例。

3)我们说变量v是共享的,当且仅当它的一个实例被一个以上的线程引用。

示例代码

/* $begin sharing */ #include "csapp.h" #define N 2 void *thread(void *vargp); char **ptr; /* global variable */ int main() { int i; pthread_t tid; char *msgs[N] = { "Hello from foo", "Hello from bar" }; ptr = msgs; for (i = 0; i < N; i++) Pthread_create(&tid, NULL, thread, (void *)i); Pthread_exit(NULL); } void *thread(void *vargp) { int myid = (int)vargp; //cnt是共享的,而myid不是共享的 static int cnt = 0; printf("[%d]: %s (cnt=%d)\n", myid, ptr[myid], ++cnt); } /* $end sharing */ 

2、用信号量同步

当对同一共享变量,有多个线程进行更新时,由于每一次更新,对该变量来说,都有“加载到寄存器,更新之,存储写回到存储器”这个过程,多个线程操作时,便会产生错位,混乱的情况,有必要对共享变量作一保护,使这个更新操作具有原子性。

信号量s是具有非页整数值的全局变量,只能由两种特殊的操作来处理,称为PV操作。

P(s):

  while (s <= 0); s–;

     V (s): s++;

    The P operation waits for the semaphore s to become nonzero, and then decrements it.The V operation increments s.

1)基本思想是,将每个共享变量(或者相关共享变量集合)与一个信号量s(初始值1)联系起来,然后用P(s),V(s)操作将相应的临界区(一段代码)包围起来。以这种方法来保护共享变量的信号量叫做二进制信号量(binary semaphore),因为值总是10

The definitions of P and V ensure that a running program can never enter a state where a properly initialized semaphore has a negative value.

11.4.4有posix信号量的简介。

2)二进制信号量通常叫做互斥锁,在互斥锁上执行一个P操作叫做加锁,V操作叫做解锁;一个已经对一个互斥锁加锁而还没有解锁的线程被称为占用互斥锁。

3、用信号量来调度共享资源

这种情况下,一个线程用信号量来通知另一个线程,程序状态中的某个条件已经为真了。如生产者消费者问题。

示例代码

#ifndef __SBUF_H__
#define __SBUF_H__

#include "csapp.h"

/* $begin sbuft */
typedef struct {
    int *buf;          /* Buffer array */         
    int n;             /* Maximum number of slots */
    int front;         /* buf[(front+1)%n] is first item */
    int rear;          /* buf[rear%n] is last item */
    sem_t mutex;       /* Protects accesses to buf */
    sem_t slots;       /* Counts available slots */
    sem_t items;       /* Counts available items */
} sbuf_t;
/* $end sbuft */

void sbuf_init(sbuf_t *sp, int n);
void sbuf_deinit(sbuf_t *sp);
void sbuf_insert(sbuf_t *sp, int item);
int sbuf_remove(sbuf_t *sp);

#endif /* __SBUF_H__ */
//source code
/* $begin sbufc */
#include "csapp.h"
#include "sbuf.h"

/* Create an empty, bounded, shared FIFO buffer with n slots */
/* $begin sbuf_init */
void sbuf_init(sbuf_t *sp, int n)
{
    sp->buf = Calloc(n, sizeof(int)); 
    sp->n = n;                       /* Buffer holds max of n items */
    sp->front = sp->rear = 0;        /* Empty buffer iff front == rear */
    Sem_init(&sp->mutex, 0, 1);      /* Binary semaphore for locking */
    Sem_init(&sp->slots, 0, n);      /* Initially, buf has n empty slots */
    Sem_init(&sp->items, 0, 0);      /* Initially, buf has zero data items */
}
/* $end sbuf_init */

/* Clean up buffer sp */
/* $begin sbuf_deinit */
void sbuf_deinit(sbuf_t *sp)
{
    Free(sp->buf);
}
/* $end sbuf_deinit */

/* Insert item onto the rear of shared buffer sp */
/* $begin sbuf_insert */
void sbuf_insert(sbuf_t *sp, int item)
{
    P(&sp->slots);                          /* Wait for available slot */
    P(&sp->mutex);                          /* Lock the buffer */
    sp->buf[(++sp->rear)%(sp->n)] = item;   /* Insert the item */
    V(&sp->mutex);                          /* Unlock the buffer */
    V(&sp->items);                          /* Announce available item */
}
/* $end sbuf_insert */

/* Remove and return the first item from buffer sp */
/* $begin sbuf_remove */
int sbuf_remove(sbuf_t *sp)
{
    int item;
    P(&sp->items);                          /* Wait for available item */
    P(&sp->mutex);                          /* Lock the buffer */
    item = sp->buf[(++sp->front)%(sp->n)];  /* Remove the item */
    V(&sp->mutex);                          /* Unlock the buffer */
    V(&sp->slots);                          /* Announce available slot */
    return item;
}
/* $end sbuf_remove */
/* $end sbufc */

参考

[1] http://www.cnblogs.com/mydomain/archive/2011/07/10/2102147.html

    原文作者:浪里飞
    原文地址: https://www.cnblogs.com/mydomain/archive/2011/07/10/2102169.html
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞