Linux学习 :多线程编程
1.Linux进程与线程()
进程:通过fork创建子进程与创建线程之间是有区别的:fork创建出该进程的一份拷贝,创建时额外申请了新的内存空间以及存储代码段、数据段、BSS段、堆、栈空间,
这个新进程拥有自己的变量和自己的PID,它的时间调度是独立的,它的执行几乎完全独立于父进程,进程可以看成一个资源的基本单位。
线程: 通过pthread_create创建线程在用户空间申请自己的栈空间,而与同进程的其他线程共享其他的地址空间,
线程是程序调度的基本单位,一个进程内部的线程之间共享进程获得的时间片。
2.线程创建:
#include <pthread.h>
int pthread_create(pthread_t *thread, pthread_attr_t *attr, void *(*start_routine)(void *), void *arg);
参数说明:
thread:指向pthread_create类型的指针,用于引用新创建的线程。
attr:用于设置线程的属性,一般不需要特殊的属性,所以可以简单地设置为NULL。
*(*start_routine)(void *):传递新线程所要执行的函数地址。
arg:新线程所要执行的函数的参数。
调用如果成功,则返回值是0,如果失败则返回错误代码。
3.线程终止
#include <pthread.h>
void pthread_exit(void *retval);
参数说明:
retval:返回指针,指向线程向要返回的某个对象。
线程通过调用pthread_exit函数终止执行,并返回一个指向某对象的指针。注意:绝不能用它返回一个指向局部变量的指针,因为线程调用该函数后,这个局部变量就不存在了,这将引起严重的程序漏洞。
4.线程同步
#include <pthread.h>
int pthread_join(pthread_t th, void **thread_return);
参数说明:
th:等待将要退出的线程的ID,线程通过pthread_create返回的标识符来指定。
thread_return:一个指针,指向另一个指针,而后者指向线程的返回值。
5.一个简单的多线程Demo:
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h> void *thread_function(void *arg); char message[] = "Hello World"; int main()
{
int res;
pthread_t a_thread;
void *thread_result; res = pthread_create(&a_thread, NULL, thread_function, (void *)message);
if (res != )
{
perror("Thread creation failed!");
exit(EXIT_FAILURE);
} printf("Waiting for thread to finish.../n"); res = pthread_join(a_thread, &thread_result);
if (res != )
{
perror("Thread join failed!/n");
exit(EXIT_FAILURE);
} printf("Thread joined, it returned %s/n", (char *)thread_result);
printf("Message is now %s/n", message); exit(EXIT_FAILURE);
} void *thread_function(void *arg)
{
printf("thread_function is running. Argument was %s/n", (char *)arg);
sleep();
strcpy(message, "Bye!");
pthread_exit("Thank you for your CPU time!");
}
编译这个程序时,需要定义宏_REENTRANT:
gcc -D_REENTRANT thread1.c -o thread1 –lpthread
运行这个程序:
$ ./thread1输出:
thread_function is running. Argument was Hello World
Waiting for thread to finish...
Thread joined, it returned Thank you for your CPU time!
Message is now Bye!
注意:
pthread_exit(void *retval)本身返回的就是指向某个对象的指针,因此,pthread_join(pthread_t th, void **thread_return);中的thread_return是二级指针,指向线程返回值的指针。可以看到,我们创建的新线程修改的数组message的值,而原先的线程也可以访问该数组。如果我们调用的是fork而不是pthread_create,就不会有这样的效果了。原因是fork创建子进程之后,子进程会拷贝父进程,两者分离,相互不干扰,而线程之间则是共享进程的相关资源。
6.用信号量进行同步
(1).信号量创建
#include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned int value);
参数说明:
sem:信号量对象。
pshared:控制信号量的类型,0表示这个信号量是当前进程的局部信号量,否则,这个信号量就可以在多个进程之间共享。
value:信号量的初始值。
(2).信号量控制
#include <semaphore.h>
int sem_wait(sem_t *sem);
int sem_post(sem_t *sem);
sem_post的作用是以原子操作的方式给信号量的值加1。
sem_wait的作用是以原子操作的方式给信号量的值减1,但它会等到信号量非0时才会开始减法操作。如果对值为0的信号量调用sem_wait,这个函数就会等待,直到有线程增加了该信号量的值使其不再为0。
(3).信号量销毁
#include <semaphore.h>
int sem_destory(sem_t *sem);
这个函数的作用是,用完信号量后对它进行清理,清理该信号量所拥有的资源。如果你试图清理的信号量正被一些线程等待,就会收到一个错误。
与大多数Linux函数一样,这些函数在成功时都返回0。
下面编码实现输入字符串,统计每行的字符个数,以“end”结束输入:
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <semaphore.h> #define SIZE 1024 void *thread_function(void *arg); char buffer[SIZE];
sem_t sem; int main()
{
int res;
pthread_t a_thread;
void *thread_result; res = sem_init(&sem, , );
if (res != )
{
perror("Sem init failed");
exit(EXIT_FAILURE);
} res = pthread_create(&a_thread, NULL, thread_function, NULL);
if (res != )
{
perror("Thread create failed");
exit(EXIT_FAILURE);
} printf("Input some text. Enter 'end' to finish/n"); while (scanf("%s", buffer))
{
sem_post(&sem);
if (strncmp("end", buffer, ) == )
break;
} printf ("/nWaiting for thread to finish.../n"); res = pthread_join(a_thread, &thread_result);
if (res != )
{
perror("Thread join failed");
exit(EXIT_FAILURE);
} printf ("Thread join/n"); sem_destroy(&sem); exit(EXIT_SUCCESS);
} void *thread_function(void *arg)
{
sem_wait(&sem); //计数为0时在此等待
while (strncmp("end", buffer, ) != )
{
printf("You input %d characters/n", strlen(buffer));
sem_wait(&sem); //计数为0时在此等待
}
pthread_exit(NULL);
}
7.用互斥量进行线程同步
另一种用在多线程程序中同步访问的方法是使用互斥量。它允许程序员锁住某个对象,使得每次只能有一个线程访问它。为了控制对关键代码的访问,必须在进入这段代码之前锁住一个互斥量,然后在完成操作之后解锁它。
用于互斥量的基本函数和用于信号量的函数非常相似:
#include <pthread.h>
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t, *mutexattr);
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
int pthread_mutex_destory(pthread_mutex_t *mutex);
与其他函数一样,成功时返回0,失败时将返回错误代码,但这些函数并不设置errno,所以必须对函数的返回代码进行检查。互斥量的属性设置这里不讨论,因此设置成NULL。
用互斥量来重写刚才的代码如下:
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <semaphore.h> #define SIZE 1024
char buffer[SIZE]; void *thread_function(void *arg);
pthread_mutex_t mutex; int main()
{
int res;
pthread_t a_thread;
void *thread_result; res = pthread_mutex_init(&mutex, NULL);
if (res != )
{
perror("Mutex init failed!");
exit(EXIT_FAILURE);
} res = pthread_create(&a_thread, NULL, thread_function, NULL);
if (res != )
{
perror("Thread create failed!");
exit(EXIT_FAILURE);
} printf("Input some text. Enter 'end' to finish/n"); while ()
{
pthread_mutex_lock(&mutex);
scanf("%s", buffer);
pthread_mutex_unlock(&mutex);
if (strncmp("end", buffer, ) == )
break;
sleep();
} res = pthread_join(a_thread, &thread_result);
if (res != )
{
perror("Thread join failed!");
exit(EXIT_FAILURE);
} printf("Thread joined/n"); pthread_mutex_destroy(&mutex); exit(EXIT_SUCCESS);
} void *thread_function(void *arg)
{
sleep(); while ()
{
pthread_mutex_lock(&mutex);
printf("You input %d characters/n", strlen(buffer));
pthread_mutex_unlock(&mutex);
if (strncmp("end", buffer, ) == )
break;
sleep();
}
}
8.线程的属性
如在前面的示例中,都使用的pthread_join同步线程,但其实有些情况并不需要。如:主线程为服务线程,而第二个线程为数据备份线程,备份工作完成之后,第二个线程可以直接终止了,它没有必要再返回到主线程中。因此,我们可以创建一个“脱离线程”。
下面介绍几个常用的函数:
(1)int pthread_attr_init (pthread_attr_t* attr);
功能:对线程属性变量的初始化。
attr:线程属性。
函数返回值:成功:0,失败:-1
(2) int pthread_attr_setscope (pthread_attr_t* attr, int scope);
功能:设置线程绑定属性。
attr:线程属性。
scope:PTHREAD_SCOPE_SYSTEM(绑定);PTHREAD_SCOPE_PROCESS(非绑定)
函数返回值:成功:0,失败:-1
(3) int pthread_attr_setdetachstate (pthread_attr_t* attr, int detachstate);
功能:设置线程分离属性。
attr:线程属性。
detachstate:PTHREAD_CREATE_DETACHED(分离);PTHREAD_CREATE_JOINABLE(非分离)
函数返回值:成功:0,失败:-1
(4) int pthread_attr_setschedpolicy(pthread_attr_t* attr, int policy);
功能:设置创建线程的调度策略。
attr:线程属性;
policy:线程调度策略:SCHED_FIFO、SCHED_RR和SCHED_OTHER。
函数返回值:成功:0,失败:-1
(5) int pthread_attr_setschedparam (pthread_attr_t* attr, struct sched_param* param);
功能:设置线程优先级。
attr:线程属性。
param:线程优先级。
函数返回值:成功:0,失败:-1
(6) int pthread_attr_destroy (pthread_attr_t* attr);
功能:对线程属性变量的销毁。
attr:线程属性。
函数返回值:成功:0,失败:-1
(7)其他
int pthread_attr_setguardsize(pthread_attr_t* attr,size_t guardsize);//设置新创建线程栈的保护区大小。
int pthread_attr_setinheritsched(pthread_attr_t* attr, int inheritsched);//决定怎样设置新创建线程的调度属性。
int pthread_attr_setstack(pthread_attr_t* attr, void* stackader,size_t stacksize);//两者共同决定了线程栈的基地址以及堆栈的最小尺寸(以字节为单位)。
int pthread_attr_setstackaddr(pthread_attr_t* attr, void* stackader);//决定了新创建线程的栈的基地址。
int pthread_attr_setstacksize(pthread_attr_t* attr, size_t stacksize);//决定了新创建线程的栈的最小尺寸(以字节为单位)。
例:创建优先级为10的线程。
pthread_attr_t attr;
struct sched_param param;
pthread_attr_init(&attr);
pthread_attr_setscope (&attr, PTHREAD_SCOPE_SYSTEM); //绑定
pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED); //分离
pthread_attr_setschedpolicy(&attr, SCHED_RR);
param.sched_priority = 10;
pthread_attr_setschedparam(&attr, ¶m);
pthread_create(xxx, &attr, xxx, xxx);
pthread_attr_destroy(&attr);
下面实现一个脱离线程的程序,创建一个线程,其属性设置为脱离状态。子线程结束时,要使用pthread_exit,原来的主线程不再等待与子线程重新合并。代码如下:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h> void *thread_function(void *arg); char message[] = "Hello World";
int thread_finished = ; int main()
{
int res;
pthread_t a_thread;
pthread_attr_t thread_attr; res = pthread_attr_init(&thread_attr);
if (res != )
{
perror("Attribute creation failed");
exit(EXIT_FAILURE);
} res = pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED);
if (res != )
{
perror("Setting detached attribute failed");
exit(EXIT_FAILURE);
} res = pthread_create(&a_thread, &thread_attr, thread_function, (void*)message);
if (res != )
{
perror("Thread creation failed");
exit(EXIT_FAILURE);
} pthread_attr_destroy(&thread_attr);
while(!thread_finished)
{
printf("Waiting for thread to say it's finished.../n");
sleep();
} printf("Other thread finished, bye!/n");
exit(EXIT_SUCCESS);
} void *thread_function(void *arg)
{
printf("thread_function is running. Argument was %s/n", (char *)arg);
sleep();
printf("Second thread setting finished flag, and exiting now/n");
thread_finished = ;
pthread_exit(NULL);
}
9.取消一个线程
与信号处理一样,线程可以在被要求终止时改变其行为。
用于请求一个线程终止的函数:
#include <pthread.h>
int pthread_cancel(pthread_t thread);
提供一个线程标识符就可以发送请求来取消它。
线程可以用pthread_setcancelstate设置自己的取消状态。
#include <pthread.h>
int pthread_setcancelstate(int state, int *oldstate);
参数说明:
state:可以是PTHREAD_CANCEL_ENABLE允许线程接收取消请求,也可以是PTHREAD_CANCEL_DISABLE忽略取消请求。
oldstate:获取先前的取消状态。如果对它没兴趣,可以简单地设置为NULL。如果取消请求被接受了,线程可以进入第二个控制层次,用pthread_setcanceltype设置取消类型。
#include <pthread.h>
int pthread_setcanceltype(int type, int *oldtype);
参数说明:
type:可以取PTHREAD_CANCEL_ASYNCHRONOUS,它将使得在接收到取消请求后立即采取行动;另一个是PTHREAD_CANCEL_DEFERRED,它将使得在接收到取消请求后,一直等待直到线程执行了下述函数之一后才采取行动:pthread_join、pthread_cond_wait、pthread_cond_timedwait、pthread_testcancel、sem_wait或sigwait。
oldtype:允许保存先前的状态,如果不想知道先前的状态,可以传递NULL。
默认情况下,线程在启动时的取消状态为PTHREAD_CANCEL_ENABLE,取消类型是PTHREAD_CANCEL_DEFERRED。
示例:主线程向它创建的线程发送一个取消请求
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h> void *thread_function(void *arg); int main()
{
int res;
pthread_t a_thread;
void *thread_result; res = pthread_create(&a_thread, NULL, thread_function, NULL);
if (res != )
{
perror("Thread create failed!");
exit(EXIT_FAILURE);
} sleep();
printf("Canceling thread.../n"); res = pthread_cancel(a_thread);
if (res != )
{
perror("Thread cancel failed!");
exit(EXIT_FAILURE);
} printf ("Waiting for thread to finished.../n"); res = pthread_join(a_thread, &thread_result);
if (res != )
{
perror ("Thread join failed!");
exit(EXIT_FAILURE);
} printf("Thread canceled!"); exit(EXIT_FAILURE);
} void *thread_function(void *arg)
{
int i;
int res; res = pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
if (res != )
{
perror("Thread setcancelstate failed!");
exit(EXIT_FAILURE);
} res = pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
if (res != )
{
perror("Thread setcanceltype failed!");
exit(EXIT_FAILURE);
} printf("thread_function is running.../n"); for (i = ; i < ; i++)
{
printf("Thread is still running (%d).../n", i);
sleep();
}
pthread_exit();
}
10.多线程
上面都仅仅是创建了一个线程--,现在我们来演示一下如何创建一个多线程的程序:
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h> #define NUM 6 void *thread_function(void *arg); int main()
{
int res;
pthread_t a_thread[NUM];
void *thread_result;
int index; for (index = ; index < NUM; ++index) {
res = pthread_create(&a_thread[index], NULL, thread_function, (void *)index);
if (res != )
{
perror("Thread create failed!");
exit(EXIT_FAILURE);
}
sleep();
} printf("Waiting for threads to finished.../n"); for (index = NUM - ; index >= ; --index)
{
res = pthread_join(a_thread[index], &thread_result);
if (res == )
{
printf("Picked up a thread:%d/n", index + );
}
else
{
perror("pthread_join failed/n");
}
} printf("All done/n"); exit(EXIT_SUCCESS);
} void *thread_function(void *arg)
{
int my_number = (int)arg;
int rand_num; printf("thread_function is running. Argument was %d/n", my_number);
rand_num = + (int)(9.0 * rand()/(RAND_MAX + 1.0));
sleep(rand_num);
printf("Bye from %d/n", my_number);
pthread_exit(NULL);
}
编译这个程序:
gcc -D_REENTRANT thread7.c -o thread7 –lpthread
运行这个程序:
$ ./thread7
thread_function is running. Argument was 0
thread_function is running. Argument was 1
thread_function is running. Argument was 2
thread_function is running. Argument was 3
thread_function is running. Argument was 4
Bye from 1
thread_function is running. Argument was 5
Waiting for threads to finished...
Bye from 5
Picked up a thread:6
Bye from 0
Bye from 2
Bye from 3
Bye from 4
Picked up a thread:5
Picked up a thread:4
Picked up a thread:3
Picked up a thread:2
Picked up a thread:1
All done
Linux学习 :多线程编程的更多相关文章
- Linux下多线程编程遇到的一些问题
今天在学习了Linux的多线程编程的基础的知识点.于是就试着做了一个简单的Demo.本以为会得到预期的结果.不成想却遇到了意想不到的问题. 代码展示 我的C 代码很简单,就是一个简单的示例程序,如下: ...
- [转]Linux 的多线程编程的高效开发经验
Linux 平台上的多线程程序开发相对应其他平台(比如 Windows)的多线程 API 有一些细微和隐晦的差别.不注意这些 Linux 上的一些开发陷阱,常常会导致程序问题不穷,死锁不断.本文中我们 ...
- 转载自~浮云比翼:Step by Step:Linux C多线程编程入门(基本API及多线程的同步与互斥)
Step by Step:Linux C多线程编程入门(基本API及多线程的同步与互斥) 介绍:什么是线程,线程的优点是什么 线程在Unix系统下,通常被称为轻量级的进程,线程虽然不是进程,但却可 ...
- Linux 的多线程编程的高效开发经验(转)
http://www.ibm.com/developerworks/cn/linux/l-cn-mthreadps/ 背景 Linux 平台上的多线程程序开发相对应其他平台(比如 Windows)的多 ...
- Linux 的多线程编程的高效开发经验
http://www.ibm.com/developerworks/cn/linux/l-cn-mthreadps/ 背景 Linux 平台上的多线程程序开发相对应其他平台(比如 Windows)的多 ...
- Linux下多线程编程
一.为什么要引入线程? 使用多线程的理由之一是和进程相比,它是一种非常"节俭"的多任务操作方式.在Linux系统下,启动一个新的进程必须分配给它独立的地址空间,建立众多的数据表来维 ...
- Linux C多线程编程-线程互斥
Linux下的多线程编程需要注意的是程序需要包含头文件pthread.h,在生成可执行文件的时候需要链接库libpthread.a或者libpthread.so. 线程创建函数: pthread_cr ...
- Linux下多线程编程-信号量
今天来谈谈线程的同步--信号量. 首先来看看一些概念性的东西: 如进程.线程同步,可理解为进程或线程A和B一块配合,A执行到一定程度时要依靠B的某个结果,于是停下来,示意B运行:B依言执行,再将结果给 ...
- Step by Step:Linux C多线程编程入门(基本API及多线程的同步与互斥)
介绍:什么是线程,线程的优点是什么 线程在Unix系统下,通常被称为轻量级的进程,线程虽然不是进程,但却可以看作是Unix进程的表亲,同一进程中的多条线程将共享该进程中的全部系统资源,如虚拟地址空间, ...
- linux c 多线程编程
linux 下 c 语言多线程: /* 06.3.6 Mhello1.c Hello,world -- Multile Thread */ #include<stdio.h> #inclu ...
随机推荐
- 记录python接口自动化测试--简单总结一下学习过程(第十目)
至此,从excel文件中循环读取接口到把测试结果写进excel,一个简易的接口自动化测试框架就完成了.大概花了1周的时间,利用下班和周末的时间来理顺思路.编写调试代码,当然现在也还有很多不足,例如没有 ...
- C# 连接PDA扫码枪
Symbol扫码枪开发文档 一.主要功能 实现symbol扫码枪和PC数据交互. 二.运行环境 SymbolMC32NO 三.开发工具与软件 开发环境:Win7 开发语言:C# 数据库:Sql Ser ...
- 雷林鹏分享:jQuery EasyUI 树形菜单 - 创建复杂树形网格
jQuery EasyUI 树形菜单 - 创建复杂树形网格 树形网格(TreeGrid)可以展示有限空间上带有多列和复杂数据电子表格.本教程将演示如何将表格数据排列在分割的网格和多行表头中,以便组织共 ...
- Hadoop – The Definitive Guide Examples,,IntelliJ
IntelliJ Project for Building Hadoop – The Definitive Guide Examples http://vichargrave.com/intellij ...
- shiro中编码/加密
在涉及到密码存储问题上,应该加密/生成密码摘要存储,而不是存储明文密码.比如之前的600w csdn账号泄露对用户可能造成很大损失,因此应加密/生成不可逆的摘要方式存储. 5.1 编码/解码 Shir ...
- leetcode-algorithms-25 Reverse Nodes in k-Group
leetcode-algorithms-25 Reverse Nodes in k-Group Given a linked list, reverse the nodes of a linked l ...
- java中equals与==的比较
总结来说: 1)对于==,如果作用于基本数据类型的变量,则直接比较其存储的 “值”是否相等: 如果作用于引用类型的变量,则比较的是所指向的对象的地址 2)对于equals方法,注意:equals方法不 ...
- nyoj-1015-二分图判定
二部图 时间限制:1000 ms | 内存限制:65535 KB 难度:1 描述 二部图又叫二分图,我们不是求它的二分图最大匹配,也不是完美匹配,也不是多重匹配,而是证明一个图是不是二部图.证 ...
- ssh -vT git@github.com get “ No such file or directory” 错误
在"Are you sure you want to continue connnecting"的时候回复y,不要打空格跳过 参考:http://stackoverflow.com ...
- 2017-4-13/MySQL
1. mysql一般的连接方式都有哪些,各自优缺点. MySQL:过程式风格,最常用. MySQLi:MySQL的增强扩展,提供了过程化和面向对象两种风格的API,增加了预编译和参数绑定等新特性, 但 ...