线程(上)

什么是线程?
(1)在一个程序里的一个执行路线叫做线程。更准确的定义是:线程是一个进程内部的控制序列。
(2)根据运行环境和调度者的身份,线程可以分为内核线程和用户线程。内核线程:运行在内核空间,由内核来调度。用户线程:运行在用户空间,由线程库来调度。
(3)线程的实现方式:完全在用户空间实现(创建和调度线程无需内核的干预,速度相当快);完全由内核调度;双层调度(实现两种实现模式的混合体,不但不会消耗过多的资源,而且线程切换速度也比较快,同时可以充分利用多处理器的优势。

线程优缺点?

优点:
(1)创建新线程的代价比创建新进程小的多
(2)与进程相比,线程之间的切换需要操作系统做的工作要少很多
(4)能充分利用多处理器的可并行数量。
(5)计算密集型应用。
(6)I/O密集型应用,为了提高性能,将I/O操作重叠。
缺点:
(1)性能损失 线程不是越多越好,合适最好。
多线程之间有切换成本,如果切换的成本大于并行的成本,并行更好
(2)健壮性降低 线程之间是缺乏保护的。
(3)缺乏访问控制
(4)编程难度提高

线程和进程的区别?

(1)进程是具有独立功能的程序关于某个数据集合的一次运行活动。
可以申请和拥有系统资源,是一个动态的概念,是活动的实体。
线程基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈)。
(2)通常在一个进程中可以包含若干个线程,它们可以利用进程所拥有的资源。在引入线程的操作系统中,通常都是把进程作为分配资源的基本单位,而把线程作为独立运行和独立调度的基本单位。
(3)子进程和父进程拥有不同的代码和数据空间,而多个线程则共享数据空间。某进程内的线程在其它进程不可见。
(4)线程上下文切换比进程上下文切换要快得多

POSIX线程库:
与线程有关的函数构成了一个完整的系列,大多数以pthred开头。
(1)创建线程
这里写图片描述
实现:
这里写图片描述
查看线程信息:

[a@localhost thread]$ ps axu | grep mythread
a          3515  0.0  0.0 103252   828 pts/3    S+   17:17   0:00 grep mythread
[a@localhost thread]$ ps -aL | head -n1
   PID    LWP TTY          TIME CMD

(2)获得线程自身ID

pthread_t pthread_self(void);

这里写图片描述
结果:

[a@localhost thread]$ ./create
i am main thread!pid:3546,thread id:3355490048
i am thread 1!pid:3546,thread id:3355481856
i am main thread!pid:3546,thread id:3355490048
i am thread 1!pid:3546,thread id:3355481856

(3)线程终止
三种方式实现
(a)从线程函数return,这种方法对主线程不适用,从main函数return
相当于调用exit。(value_ptr所指向的单元内容存放的是thread线程函数的返回值)
(b)线程可以调用pthread_exit终止自己。(value_ptr所指向单元的内容是传给pthread_exit的参数)

#include<stdio.h>
#include<pthread.h>
#include<unistd.h>
void *thread_run(void *arg)
{
  printf("new thread running !pid: %d,thread id: %u\n",getpid(),pthread_self());
  sleep(200);
  pthread_exit((void*)123);
  //exit(1);
//  return (void*) 5;
}
int main()
{
    pthread_t id;
    pthread_create(&id,NULL,thread_run,NULL);
     printf("main thread running !pid: %d,thread id: %u\n",getpid(),pthread_self());
    sleep(1);
    pthread_cancel(id);
    void *code;
    int ret =pthread_join(id,&code);
    if(ret == 0)
    {
       printf("wait new thread success!,%d\n",(int)code);
    }
    return 0;
}
[a@localhost thread]$ ./thread
main thread running !pid: 7219,thread id: 2158917376
new thread running !pid: 7219,thread id: 2158909184
wait new thread success!,-1
[a@localhost thread]$ 

(c)一个线程可以调用pthread_cancel终止同一进程中的另一个线程。(value_ptr所指向单元的内容存放的是一个常数PTHREAD_CANCELED)

[a@localhost thread]$ ./thread
I am thread 1,id is:140022436087552
main thread run new thread ret:-1
[a@localhost thread]$ cat thread.c
#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>
void *thread_run(void *arg)
{
    printf("I am %s,id is:%lu\n",(char*)arg,pthread_self());
    //sleep(3);
    pthread_exit((void*)123);
}
int main()
{
    pthread_t tid;
    pthread_create(&tid,NULL,thread_run,"thread 1
    //sleep(5);
    pthread_cancel(tid);
    void *ret;
    pthread_join(tid,&ret);
    printf("main thread run new thread ret:%d\n",(int)ret);
    return 21;
}

线程创建后sleep5秒后,结果为:

[a@localhost thread]$ ./thread
I am thread 1,id is:140050701547264
main thread run new thread ret:123
[a@localhost thread]$ 

原因:主线程在sleep期间,线程已经退出,不是因为取消而结束,而因为退出而结束
分离线程
在任何一个时间点上,线程是可结合或者是分离的。默认情况下新创建的线程是可结合的。
一个可结合的线程能够被其它线程回收其资源和杀死。为避免内存泄漏,每个可结合的线程要么被显示地回收,即调用pthread_join;要么通过调用pthread_detach函数被分离。

一个分离的线程是不能被其它线程回收或杀死,它的存储器资源在它终止时由系统自动释放。
这里写图片描述
这里写图片描述
同步与互斥
同步强调顺序性和协同。一般都是在互斥的前提下,但有些场景也不需要互斥。
比如生活中这样的场景,门上挂着钥匙,一次只有一个人可以拿着钥匙开门进入,此时其他人在门外等待,门外的人按照一定的规则按顺序排队进门就是同步。
门内资源被某人占用,其他人在门外等待,这就是所谓的互斥。
互斥量:
也叫互斥锁,是实现同步的重要工具。在线程访问共享资源前对互斥量进行设置加锁(拿钥匙),在线程访问共享资源结束后释放互斥量解锁(交出钥匙)。

一个线程在占用资源时(钥匙),任何其他再试图申请同一份锁资源的线程,都会被阻塞。直到当前进程释放该锁资源。在互斥锁被释放时,所以因为该锁被阻塞的线程都会变成可运行状态,第一个变为运行的线程就可以对互斥量加锁,其它线程就会看到锁资源依然被占用,只能回去再次等待。
在这种方式下可以达到一个时刻只有一个线程可以向前执行,并且多线程时会按照一定的顺序进行协同。
相关函数:
(1)创建互斥量

动态分配:int pthread_mutex_init(pthread_mutex_t* restrict mutex,const pthread_mutexattr_t* restrict attr)

参数:mutex 要初始化的互斥量
attr NULL

静态分配:pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

(2)加锁和解锁

加锁:int pthread_mutex_lock(pthread_mutex_t *mutex);
解锁:int pthread_mutex_unlock(pthread_mutex_t *mutex);

返回值:成功返回0,失败返回错误号
(3)销毁互斥量

int pthread_mutex_destroy(pthread_mutex_t *mutex);

注意:
(a)使用PTHREAD_MUTEX_INITIALIZER初始化的互斥量不需要销毁。
(b)不要销毁一个已经加锁的互斥量
(c)已经销毁的互斥量,要确保后面不会有线程再尝试加锁。
这里写图片描述
结果:

thread 3 sells ticket:4
thread 2 sells ticket:3
thread 1 sells ticket:2
thread 1 sells ticket:1
thread 2 sells ticket:0
thread 3 sells ticket:-1

应用互斥量改进上面的售票系统:
这里写图片描述
结果:
这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值