对线程的深入学习(一)

来源:互联网 发布:腾讯数据分析待遇 编辑:程序博客网 时间:2024/05/22 06:40

对线程概念的理解:

1. 线程就是程序的执行路线,即进程内部的控制序列,或者说是进程的子任务。
2. 线程,轻量级,不拥有自己独立的内存资源,共享进程的代码区、数据区、堆区(注意没有栈区)、环境变量和命令行参数、文件描述符、信号处理函数、  当前目录、用户ID和组ID等资源。
3. 线程拥有自己独立的栈,因此也有自己独立的局部变量。
4. 一个进程可以同时拥有多个线程,  即同时被系统调度的多条执行路线, 但至少要有一个主线程。

更深入的理解:
5. 线程是进程的一个实体,可作为系统独立调度和分派的基本单位。
6. 线程有不同的状态,系统提供了多种线程控制原语,如创建线程、销毁线程等等。
7. 线程不拥有自己的资源,只拥有从属于进程的全部资源,所有的资源分配都是面向进程的。
8. 一个进程中可以有多个线程并发地运行。 它们可以执行相同的代码,也可以执行不同的代码。
9. 同一个进程的多个线程都在同一个地址空间内活动,因此相对于进程,线程的系统开销小,任务切换快
10. 线程间的数据交换不需要依赖于类似IPC的特殊通信机制, 简单而高效。
11. 每个线程拥有自己独立的线程ID、寄存器信息、函数栈、  错误码和信号掩码。
12. 线程之间存在优先级的差异。

自己的见解:

       进程严格意义上讲只是进程映像(如下图),并不包括程序运行的那个动作,而线程则是程序的执行路线pthread_create的第三个参数就是实现这个动作的。打个比方进程就像是一桌子饭,线程就是吃饭的动作,多个人吃饭就是多线程,饭只有一份,但是每个人都吃到自己的肚子里,肚子就类似线程的临时存储区域(栈),在线程中只有栈是独立的,其他的都是共享进程的。


1. 创建线程

int pthread_create (pthread_t* restrict thread,                    const pthread_attr_t* restrict attr,                    void* (*start_routine) (void*),                    void* restrict arg);
      thread - 线程ID,输出参数。pthread_t 即 unsigned long int。
      attr - 线程属性,NULL表示缺省属性。pthread_attr_t可能是整型也可能是结构,因实现而异。
      start_routine - 线程过程函数指针,参数和返回值的类型都是void*。启动线程本质上就是调用一个函数 只不过是在一个独立的线程中调用的,函数返回即线程结束。
      arg - 传递给线程过程函数的参数。线程过程函数的调用者是系统内核,而非用户代码,因此需要在创 建线程时指定参数。
      成功返回0,失败返回错误码。

注意:
    1) restrict: C99引入的编译优化指示符,提高重复解引用同一个指针的效率。(解引用的过程其实就是在内存中查找地址想,做的优化其实质就是访问寄存器里的代替访问内存)
    2) 在pthread.h头文件中声明的函数,通常以直接返回错误码的方式表示失败,而非以错误码设置errno
    3) main函数即主线程,main函数返回即主线程结束,主线程结束即进程结束,进程一但结束其所有的线程即结束。(子进程不一定结束,可能成为孤儿)
    4) 应设法保证在线程过程函数执行期间,其参数所指向的目标持久有效。                                                                                                               
#include <stdio.h>#include <string.h>#include <unistd.h>#include <pthread.h>void* thread_proc (void* arg) {printf ("%lu线程:%s\n", pthread_self (), (char*)arg);return NULL;}int main (void) {pthread_t tid;int error = pthread_create (&tid, NULL, thread_proc,"我是快乐的子线程!");if (error) {fprintf (stderr, "pthread_create: %s\n", strerror (error));return -1;}printf ("%lu线程:我是主线程,创建了%lu线程。\n", pthread_self (),tid);//pthread_self () 自己的tidsleep (1);return 0;}


     1. sleep在这里的作用: 因为 主线程和子线程的执行顺序是不一定的,这里通过sleep让主线程等一会儿,让子线程不至于还没结束主线程就结束了

      2. 如果去掉sleep这行我们偶尔会出现子进程被打印两次的结果,原因是缓冲区的问题,我们只要加上setbuf(stdout,NULL);(关闭输出缓冲区) 就好了。因为缓冲区在进程关闭的时候刷新一次,刷新缓冲区实质上是两个步骤,第一步把缓冲区的内容显示出来,第二步就是清空,子线程打印两次的情况就是因为在第一二步骤之间切换了下线程,转去执行了一次子线程,这样就导致缓冲区又刷新了一次。

      3. 去掉sleep当然也会遇到不打印子进程的情况,这完全取决于cpu的调度

2. 线程并发的例子

//线程并发:开20个线程,每一列代表一个,打印1-500的数字#include <stdio.h>#include <string.h>#include <unistd.h>#include <pthread.h>void* thread_proc (void* arg) {size_t i;for (i = 0; i < 500; i++) {printf ("%*d\n", ((size_t)arg + 1) * 4, i + 1); //*表示跳过的意思,rag就是0-19号进程,这行意义就是让不同的线程之间有空格usleep (50000);//50ms,sleep的参数是秒}return NULL;}int main (void) {pthread_t tids[20];//20个线程size_t i;for (i = 0; i < sizeof (tids) / sizeof (tids[0]); i++) {int error = pthread_create (&tids[i], NULL, thread_proc, (void*)i);if (error) {fprintf (stderr, "pthread_create: %s\n", strerror (error));return -1;}}for (i = 0; i < sizeof (tids) / sizeof (tids[0]); i++) {int error = pthread_join (tids[i], NULL);if (error) {fprintf (stderr, "pthread_join: %s\n", strerror (error));return -1;}}return 0;}

3. 比较两个线程的方法

int pthread_equal (pthread_t t1, pthread_t t2);

若参数t1t2所标识的线程ID相等,则返回非零,否则返回0

#include <stdio.h>#include <string.h>#include <pthread.h>pthread_t g_main;//定义全局变量,用来保存主线程idvoid* ismain (void* arg) {//if (pthread_self () == g_main)if (pthread_equal (pthread_self (), g_main))printf ("我是主线程!\n");elseprintf ("我不是主线程!\n");return NULL;}int main (void) {g_main = pthread_self ();ismain (NULL);pthread_t tid;int error = pthread_create (&tid, NULL, ismain, NULL);if (error) {fprintf (stderr, "pthread_create: %s\n", strerror (error));return -1;}if ((error = pthread_join (tid, NULL)) != 0) {fprintf (stderr, "pthread_join: %s\n", strerror (error));return -1;}return 0;}

         思考:为什么不直接使用==来判断,因为某些实现的pthread_t不是unsigned long int类型,可能是结构体类型,无法通过“==”判断其相等性。用这个函数能保证在不同的机器上都不会出错。





1 0