多线程编程

第一:基本知识点
1.
进程:
进程是一个具有一定独立功能的程序的一次运行活动,同时也是资源分配的最小单元;
进程是程序执行时的一个实例,即它是程序已经执行到某种程度的数据结构的汇集。
从内核的观点看,进程的目的就是担当分配系统资源(CPU时间、内存等)的基本单位。

Linux系统是一个多进程的系统,它的进程之间具有并行性、互不干扰等特点。
也就是说,每个进程都是一个独立的运行单位,拥有各自的权利和责任。其中,各个进程都运行在独立的虚拟地址空间,因此,即使一个进程发生异常,它也不会影响到系统中的其他进程。

Linux中的进程包含3个段,分别为“数据段”、“代码段”和“堆栈段”。
“数据段”存放的是全局变量、常数以及动态数据分配的数据空间;
“代码段”存放的是程序代码的数据。
“堆栈段”存放的是子程序的返回地址、子程序的参数以及程序的局部变量等。

线程是进程的一个执行流,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。
一个进程由几个线程组成(拥有很多相对独立的执行流的用户程序共享应用程序的大部分数据结构),线程与同属一个进程的其他的线程共享进程所拥有的全部资源。

“进程——资源分配的最小单位,线程——程序执行的最小单位”
进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。

线程有自己的堆栈和局部变量,但线程没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。

进程有独立的地址空间,线程没有单独的地址空间(同一进程内的线程共享进程的地址空间)。

8.线程的有点

  1. 使用多线程的理由之一是:
    和进程相比,它是一种非常“节俭”的多任务操作方式.在Linux系统下,启动一个新的进程必须分配给它独立的地址空间,建立众多的数据表来维护它的代码段、堆栈段和数据段,这是一种"昂贵"的多任务工作方式运行于一个进程中的多个线程,它们之间使用相同的地址空间,而且线程间彼此切换所需的时间也远远小于进程间切换所需要的时间.据统计,一个进程的开销大约是一个线程开销的30倍左右

  2. 使用多线程的理由之二是:
    线程间方便的通信机制。对不同进程来说,它们具有独立的数据空间,要进行数据的传递只能通过进程间通信的方式进行,这种方式不仅费时,而且很不方便。线程则不然,由于同一进程下的线程之间共享数据空间,所以一个线程的数据可以直接为其它线程所用,这不仅快捷,而且方便

  3. 除了以上所说的优点外,多线程程序作为一种多任务、并发的工作方式,有如下优点:
    使多CPU系统更加有效.操作系统会保证当线程数不大于CPU数目时,不同的线程运行于不同的CPU上.
    改善程序结构.一个既长又复杂的进程可以考虑分为多个线程,成为几个独立或半独立的运行部分,这样的程序会利于理解和修改

线程不存在线程间的通讯(因为他们是在同一个地址空间下, 所以定义全局变量的话, 所有的线程都可以访问。)

  1. 创建线程
    #include <pthread.h>
    int pthread_create(pthread_t * tidp,const pthread_attr_tattr,void(start_rtn)(void),voidarg)
    v
    tidp:线程id
    attr: 线程属性(通常为空)
    start_rtn:线程要执行的函数
    arg:start_rtn的参数

  2. 编译
    因为pthread的库不是linux系统的库,所以在进行编译的时候要加上
    -lpthread

  3. 终止线程
    如果进程中任何一个线程中调用exit或_exit,那么整个进程都会终止。线程的正常退出方式有:
    (1) 线程从启动例程中返回
    (2) 线程可以被另一个线程终止
    (3) 线程自己调用pthread_exit函数

  4. 线程退出
    #include <pthread.h>
    void pthread_exit(void * rval_ptr)
    功能:终止调用线程
    Rval_ptr:线程退出返回值的指针

  5. 线程等待
    #include <pthread.h>

    int pthread_join(pthread_t tid,void **rval_ptr)
    功能:阻塞调用线程,直到指定的线程终止。
    Tid :等待退出的线程id
    Rval_ptr:线程退出的返回值的指针

15 线程同步
进行多线程编程,因为无法知道哪个线程会在哪个时候对共享资源进行操作,因此让如何保护共享资源变得复杂,通过下面这些技术的使用,可以解决
线程之间对资源的竞争:
1 互斥量Mutex
2 信号灯Semaphore
3 条件变量Conditions

  1. 互斥量的创建
    在Linux中, 互斥量使用类型pthread_mutex_t表示.在使用前, 要对它进行初始化:
    对于静态分配的互斥量, 可以把它设置为默认的mutex对象PTHREAD_MUTEX_INITIALIZER
    对于动态分配的互斥量, 在申请内存(malloc)之后, 通过pthread_mutex_init进行初始化, 并且在释放内存(free)前需要调用pthread_mutex_destroy

17.加锁
对共享资源的访问, 要使用互斥量进行加锁, 如果互斥量已经上了锁, 调用线程会阻塞, 直到互斥量被解锁。
int pthread_mutex_lock(pthread_mutex_t *mutex)
int pthread_mutex_trylock(pthread_mutex_t *mutex)

返回值: 成功则返回0, 出错则返回错误编号。
trylock是非阻塞调用模式, 如果互斥量没被锁住, trylock函数将对互斥量加锁, 并获得对共享资源的访问权限; 如果互斥量被锁住了,trylock函数将不会阻塞等待而直接返回EBUSY, 表示共享资源处于忙状态

18.解锁
在操作完成后,必须给互斥量解锁,也就是前面所说的释放。这样其他等待该锁的线程才有机会获得该锁,否则其他线程将会永远阻塞。
int pthread_mutex_unlock(pthread_mutex_t *mutex)

代码一(创建线程)
#include <stdio.h>
#include <pthread.h>

void delay()
{
int x = 10000, y;
while (x > 0)
{
y = 20000;
while (y > 0)
{
y–;
}
x–;
}
}

pthread_t tid[2];

void *MyThread1(void *arg)
{
//pthread_detach(pthread_self()); //线程分离,线程结束后自动释放线程资源
int old;
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &old);//属性修改为立即取消
//while (1)
//{
delay();
delay();
//sleep(2);
printf(“this is thread1!\n”);
//}
}

void *MyThread2(void *arg)
{
int i = 2;
pthread_cancel(tid[0]); //取消线程1,默认程序运行到下一个取消点结束
while (i–)
{
printf("%s\n", (char *)arg);
delay();
}
pthread_exit((void *)100); //线程退出
}

int main()
{
int ret;
char buf[32] = “helloworld”;

//创建线程,线程号、线程属性、线程函数、线程函数参数
ret = pthread_create(&tid[0], NULL, MyThread1, NULL);
if (ret != 0)   //错误处理
{
	perror("pthread_create");
}

ret = pthread_create(&tid[1], NULL, MyThread2, (void *)buf);
if (ret != 0)
{
	perror("pthread_create2");
}

void *status;
pthread_join(tid[0], &status);  //1、等待线程1结束  2、回收线程资源
printf("线程1退出 %d\n", (int)status);
pthread_join(tid[1], &status);
printf("线程2退出 %d\n", (int)status);

return 0;

}

代码二(模拟买票系统)
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>

#define SIZE 10

int Ticket = 100;
pthread_mutex_t mutex; //定义互斥锁

void delay()
{
int x = 10000, y;
while (x > 0)
{
y = 1000;
while (y > 0)
{
y–;
}
x–;
}
}

void *SaleTicket(void *arg)
{
int cur_ticket;
while (1)
{
pthread_mutex_lock(&mutex); //上锁
cur_ticket = Ticket; //读取数据
if (cur_ticket <= 0)
{
pthread_mutex_unlock(&mutex);
break;
}
delay();
printf("%d get ticket %d\n", pthread_self(), cur_ticket);
cur_ticket–;
Ticket = cur_ticket;
pthread_mutex_unlock(&mutex); //解锁
}

}

int main()
{
int i, ret;
pthread_t tid[SIZE];

pthread_mutex_init(&mutex, NULL);

for (i = 0; i < 10; i++)
{
	ret = pthread_create(&tid[i], NULL, SaleTicket, NULL);
	if (ret != 0)
	{
		perror("pthread_create");
		exit(1);
	}
}

void *status;
for (i = 0; i < SIZE; i++)
{
	pthread_join(tid[i], &status);
}

pthread_mutex_destroy(&mutex);

return 0;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值