线程基础
每个用户进程有自己的地址空间。
系统为每个用户进程创建一个 task_struct来描述该进程。
该结构体中包含了一个指针指向该进程的虚拟地址空间映射表。
实际上task_struct 和地址空间映射表一起用来,表示一个进程线程基础。
由于进程的地址空间是私有的,因此在进程间上下文切换时,系统开销比较大。
为了提高系统的性能,许多操作系统规范里引入了轻量级进程的概念,也被称为线程。
在同一个进程中创建的线程共享该进程的地址空间。
Linux里同样用task_struct来描述一个线程。线程和进程都参与统一的调度
通常线程指的是共享相同地址空间的多个任务
使用多线程的好处
大大提高了任务切换的效率线程基础
多线程通过第三方的线程库来实现
New POSIX Thread Library (NPTL)是早期Linux Threads的改进
采用1:1的线程模型
显著的提高了运行效率
NPTL线程库中提供了如下基本操作
删除线程
创建线程
控制线程
线程间同步和互斥机制
信号量
互斥锁
条件变量
Linux线程编程线程基本编程 (1)
创建线程实际上就是确定调用该线程函数的入口点,这里通常使用的函数是pthread_create()。在线程创建以后,就开始运行相关的线程函数,在该函数运行完之后,该线程也就退出了,这也是线程退出一种方法。另一种退出线程的方法是使用函数pthread_exit(),这是线程的主动行为。
由于一个进程中的多个线程是共享数据段的,因此通常在线程退出之后,退出线程所占用的资源并不会随着线程的终止而得到释放。正如进程之间可以用wait()系统调用来同步终止并释放资源一样,线程之间也有类似机制,那就是pthread_join()函数。pthread_join()可以用于将当前线程挂起来等待线程的结束。这个函数是一个线程阻塞的函数,调用它的函数将一直等待到被等待的线程结束为止,当函数返回时,被等待线程的资源就被收回。
线程示例1:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
char message[32] = “Hello World”;
void *thread_function(void *arg);
int main(int argc, char *argv[])
{
pthread_t a_thread;
void *thread_result;
if (pthread_create(&a_thread, NULL, thread_function, (void *)message) < 0)
/*使用缺省属性创建线程*/{
perror(“fail to pthread_create”);
exit(-1);
}
printf(“waiting for thread to finish\n”);
if (pthread_join(a_thread, &thread_result) < 0) // 等待线程结束
{
perror(“fail to pthread_join”);
exit(-1);
}
printf(“MESSAGE is now %s\n”, message);
return 0;
}void *thread_function(void *arg)
{
printf(“thread_function is running, argument is %s\n”, (char *)arg);
strcpy(message, “marked by thread”);
pthread_exit(“Thank you for the cpu time”);
}
线程实例2
#include<pthread.h>
#include<stdio.h>
#include<stdlib.h>
int a = 0;
pthread_mutex_t mutex;
void * hello()
{
while(1)
{
pthread_mutex_lock(&mutex);
printf("a = %d\n",a);
printf("son sleep(1)\n");
sleep(1);
pthread_mutex_unlock(&mutex);
sleep(1);
}
pthread_exit("see you");
}
int main()
{
pthread_t id;
pthread_create(&id,NULL,hello,NULL);//新建一个线程
pthread_mutex_init(&mutex,NULL);//初始化一个互斥锁
while(1)
{
pthread_mutex_lock(&mutex);//判断有没有上锁,有的话,就等待,没有的话就上锁
a++;
printf("father sleep(1)\n");
sleep(1);
pthread_mutex_unlock(&mutex);//解锁
sleep(1);
}
pthread_cancel(id);//发出线程结束命令给线程id
pthread_mutex_destroy(&mutex);//销毁互斥锁
}
编译多线程程序
# gcc -o sample sample.c -lpthread
-lpthread : 链接pthread库互斥锁线程控制,编译的时候一定要加上,否则会出现以下错误
pthread.c:(.text+0x89):对‘pthread_create’未定义的引用
pthread.c:(.text+0xa1):对‘pthread_cancel’未定义的引用
pthread.c:(.text+0xb5):对‘pthread_join’未定义的引用
互斥锁是用一种简单的加锁方法来控制对共享资源的原子操作。这个互斥锁只有两种状态,也就是上锁和解锁,可以把互斥锁看作某种意义上的全局变量。在同一时刻只能有一个线程掌握某个互斥锁,拥有上锁状态的线程能够对共享资源进行操作。若其他线程希望上锁一个已经被上锁的互斥锁,则该线程就会挂起,直到上锁的线程释放掉互斥锁为止。可以说,这把互斥锁保证让每个线程对共享资源按顺序进行原子操作。
互斥锁机制主要包括下面的基本函数。
• 互斥锁初始化:pthread_mutex_init()
• 互斥锁上锁:pthread_mutex_lock()
• 互斥锁判断上锁:pthread_mutex_trylock()
• 互斥锁接锁:pthread_mutex_unlock()
• 消除互斥锁:pthread_mutex_destroy()所需头文件 #include < pthread.h >
函数原型 ,
int pthread_mutex_init(pthread_mutex_t *mutex, pthread_mutexattr_t *attr )// 初始化互斥锁
函数参数
mutex:互斥锁属性 // NULL
attr:NULL表示缺省属性
函数返回值
成功:0
出错:-1
函数原型 int pthread_mutex_lock(pthread_mutex_t *mutex)// 申请互斥锁
函数参数 mutex:互斥锁
函数返回值
成功:0
出错:-1
int pthread_mutex_trylock(pthread_mutex_t *mutex);所需头文件 #include <pthread.h>
函数原型
int pthread_mutex_unlock(pthread_mutex_t *mutex) // 释放互斥锁
函数参数 mutex
mutex:互斥锁
函数返回值 0
成功:0
出错:-1线程互斥示例
#include<pthread.h>
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
struct mess//使用结构体可以一次性传递多个参数
{
int a;
char b;
};
void * hello(void *p)
{
struct mess *m;
m = (struct mess*)p;//该函数需要传递多个参数就使用结构体,将所有的参数都放入其中。
printf("I am xiancheng!!\n");//线程运行完后自动销毁
printf("arg1 is %d\n",(*m).a);
printf("arg2 is %c\n",(*m).b);
pthread_exit("hello() is over...");//函数的返回
}
int main()
{
pthread_t threadid;
struct mess m;
m.a = 10;
m.b = 'b';
pthread_create(&threadid,NULL,hello,(void *)&m);//创建一个线程
sleep(1);//防止子线程还没结束,main线程就已经结束了
pthread_cancel(threadid);//非阻塞函数
pthread_join(threadid,NULL);
return 0;
}
详细内容,可下载pdf文件:多线程编程.pdf