linux 下进程和线程

其实在Linux内核2.4版以前,线程的实现和管理方式就是完全按照进程方式实现的。在2.6版内核以后才有了单独的线程实现。

http://static.oschina.net/uploads/space/2015/0529/132501_6PVU_1863332.jpg

进程是资源分配的基本单位,线程是调度的基本单位。

这句经典名言已流传数十年,各种操作系统教材都可见此描述。确实如此,这就是二者的显著区别。读者请注意“基本”二字。相信有读者看到前半句的时候就在心里思考,“进程岂不是不能调度?”,非也!进程和线程都可以被调度,否则多进程程序该如何运行呢!

只是,线程是更小的可以调度的单位,也就是说,只要达到线程的水平就可以被调度了,进程自然可以被调度。它强调的是分配资源时的对象必须是进程,不会给一个线程单独分配系统管理的资源。若要运行一个任务,想要获得资源,最起码得有进程,其他子任务可以以线程身份运行,资源共享就行了。

cpp 实现线程

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <pthread.h>
#include <unistd.h>

#define ll long long

using namespace std;
void *mythread(void* arg) {
    printf("child proccess , pid = [%lld] tid = [%lld] \n",
        (ll) getpid(),
        (ll)pthread_self());
    return 0;

}
int main(void) {
    // printf("fork  子进程 %d\n", pid);
    pthread_t thread;
    int ret = pthread_create(
        &thread, 
        NULL,
        mythread,
        NULL 
    );
    if (ret ) {
        printf(" pthread_create error := [%s]\n",strerror(ret));
        return -1;
    }
    printf("current proccess , pid = [%lld]  tid = [%lld],\n",
         (ll) getpid(),
         (ll)pthread_self());
    sleep(1);
    
    return 0;
}

编译方法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
 gcc -o a.out main.cpp  -lpthread
 
 ./a.out
 :<<EOF
 lyr@DESKTOP-FSVN6C0:~$ ./aout 
current proccess , pid = [1137]  tid = [140435415930688],
child proccess , pid = [1137] tid = [140435415926528] 
 
 EOF
 
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <pthread.h>
#include <unistd.h>

#define ll long long

using namespace std;
void *mythread(void* arg) {
    int input = *(int*)arg;
    printf("input := %d\n", input);
    printf("child proccess , pid = [%lld] tid = [%lld] \n",
        (ll) getpid(),
        (ll)pthread_self());
    return 0;

}
int main(void) {
    // printf("fork  子进程 %d\n", pid);
    pthread_t thread;
    int  n = 39;
    int ret = pthread_create(
        &thread, 
        NULL,
        mythread,
        &n 
    );
    if (ret ) {
        printf(" pthread_create error := [%s]\n",strerror(ret));
        return -1;
    }
    printf("current proccess , pid = [%lld]  tid = [%lld],\n",
         (ll) getpid(),
         (ll)pthread_self());
    sleep(1);
    
    return 0;
}

查看文档

 1
 2
 3
 4
 5
 6
 7
 8
 9
10

SYNOPSIS
       #include <pthread.h>

       int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                          void *(*start_routine) (void *), void *arg);

       Compile and link with -pthread.
       
       

这个 void* 相当于java 的 Object表示任意类型

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <pthread.h>
#include <unistd.h>

#define ll long long

using namespace std;
struct Item {
    int data;
    char name[60];
};
void *mythread(void* arg) {
    Item input = *(Item*)arg;
    printf("input := %d , name = %s \n", input.data,input.name );
    printf("child proccess , pid = [%lld] tid = [%lld] \n",
        (ll) getpid(),
        (ll)pthread_self());
    return 0;

}
int main(void) {
    // printf("fork  子进程 %d\n", pid);
    pthread_t thread;
    struct Item t;
    t.data = 11;
    strcpy(t.name,"hello");
    int ret = pthread_create(
        &thread, 
        NULL,
        mythread,
        &t
    );
    if (ret ) {
        printf(" pthread_create error := [%s]\n",strerror(ret));
        return -1;
    }
    printf("current proccess , pid = [%lld]  tid = [%lld],\n",
         (ll) getpid(),
         (ll)pthread_self());
    sleep(1);
    
    return 0;
}

编译的命令太繁琐了,可以写一个编译脚本

1
2
gcc -o $1  $1.c -lpthread
# 然后直接  makegcc ./main   

ps -Lf 查看线程信息

1
2
3
4
5
6
7
lyr@DESKTOP-FSVN6C0:~$ ps -ef | grep aout
lyr       1393    12  0 19:22 pts/0    00:00:00 ./aout
lyr       1399    12  0 19:22 pts/0    00:00:00 grep --color=auto aout
lyr@DESKTOP-FSVN6C0:~$ ps -Lf  1393
UID        PID  PPID   LWP  C NLWP STIME TTY      STAT   TIME CMD
lyr       1393    12  1393  0    2 19:22 pts/0    Tl     0:00 ./aout
lyr       1393    12  1394  0    2 19:22 pts/0    Tl     0:00 ./aout

互斥锁的使用

  1. pthread_mutex_t mutex
  2. phtread_mutex_init(&mutex)
  3. pthread_mutex_unlock(&mutex)
  4. pthread_mutex_destroy(&mutex)

死锁怎么防止 【线程死锁】

  1. 自己锁自己 【 可重入锁和不可重入锁,c++ 的是不可重入的】

  2. 加锁了 不释放锁 【 java 的解决方法就是 ,finally 释放锁】

  3. 尽量不要去锁多个资源 【尽量锁住一个】

    1. 如果要锁住多个资源,也要尽量都按照顺序上锁
    2. 不要嵌套加锁 【嵌套加锁就会有问题】
    3. 访问其他锁的时候,请先释放自己的锁【不要吃着碗里的,看着锅里的】
  4. 上锁的时候加个尝试时间 【超过时间自动退出】

  5. 防止异常发生,导致没有解锁

读写锁

读共享,写阻塞

适用于 读多写少的情况。

条件变量

  • 条件变量本身不是锁, 但是可以引起阻塞,通常与互斥锁一起使用

    • 使用互斥锁保护共享数据
    • 使用条件 变量使线程阻塞,等待某个条件发生,条件满足的时候解除阻塞。
    • 条件不满足,阻塞线程
    • 条件满足,唤醒线程
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int count = 5;

//pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
//pthread_mutex_lock(&mutex);
//while(条件1)
//  pthread_cond_wait(&cond, &mutex);
//函数操作1
//pthread_mutex_unlock(&mutex);
//解释:当条件1成立的时候,执行pthread_cond_wait(&cond, &mutex)这一句,开放互斥锁,然后线程被挂起。
//      当条件1不成立的时候,跳过while循环体,执行函数操作1,然后开放互斥锁
//      即无论走哪个分支,都会放开互斥锁

//此线程先启动
void* decrement(void* arg) {
    while(1) {
        //条件等待
        pthread_mutex_lock(&mutex);
        while(count <= 0) {
            printf("count<=0,thread is hanging!\n");
            pthread_cond_wait(&cond, &mutex); 
            sleep(1);
            printf("sleep!\n");
        }
        count -= 1;
        pthread_mutex_unlock(&mutex);

        if (count == 9) {
            printf("count==9,thread1 is over!\n");
            return NULL;
        }
    }
}

//条件激发
//pthread_mutex_lock(&mutex);
//函数操作2;
//if(条件1不成立)
//  pthread_cond_signal(&cond);
//  pthread_mutex_unlock(&mutex);
//解释:先执行函数操作2,改变条件变量,使得条件1不成立的时候,执行pthread_cond_signal(&cond)这句话的意思是激发条件变量cond,使得被挂起的线程被唤醒。
//      pthread_cond_broadcast(&cond); 激发所有由于cond条件而被挂起的线程。而signal的函数则是激发一个由于条件变量cond被挂起的线程

void *increment(void *arg) {
    sleep(1);
    while(1) {
        pthread_mutex_lock(&mutex);
        count += 1;
        if(count > 0) {
            printf("count=%d, change cond state!\n", count);
            pthread_cond_signal(&cond);
        }
        pthread_mutex_unlock(&mutex);
        
        if (count == 10) {
            printf("count=10,thread is over!\n");
            return NULL;
        }
    }
}

int main(void) {
    int i1=1, i2=1;
    pthread_t id1, id2;
    pthread_mutex_init(&mutex, NULL);
    pthread_cond_init(&cond, NULL);

    pthread_create(&id1, NULL, decrement, NULL);
    pthread_create(&id2, NULL, increment, NULL);

    i2 = pthread_join(id2, NULL);
    i1 = pthread_join(id1, NULL);
    if ( (i2==0) && (i1==0) ) {
        printf("count=%d, the main thread!\n", count);
        pthread_cond_destroy(&cond);
        pthread_mutex_destroy(&mutex);
        return 0;
    }
    return -1;
}

信号量, semaphore

没错, java 的 juc 也有个 semaphore , 和 c++ 这个是一模一样的功能

允许最多多少个线程同时执行。

  1. 如果车库里面可用资源大于0,就不阻塞

  2. 如果可用资源小于等于 0 ,就阻塞当前线程,直到 可用资源大于0