进程与线程
使用多线程的理由之一是和进程相比,它是一种非常”节俭”的多任务操作方式。我们知道,在Linux系统下,启动一个新的进程必须分配给它独立的地址空间,建立众多的数据表来维护它的代码段、堆栈段和数据段,这是一种”昂贵”的多任务工作方式。而运行于一个进程中的多个线程,它们彼此之间使用相同的地址空间,共享大部分数据,启动一个线程所花费的空间远远小于启动一个进程所花费的空间,而且,线程间彼此切换所需的时间也远远小于进程间切换所需要的时间。
使用多线程的理由之二是线程间方便的通信机制。对不同进程来说,它们具有独立的数据空间,要进行数据的传递只能通过通信的方式进行,这种方式不仅费时,而且很不方便。线程则不然,由于同一进程下的线程之间共享数据空间,所以一个线程的数据可以直接为其它线程所用,这不仅快捷,而且方便。当然,数据的共享也带来其他一些问题,有的变量不能同时被两个线程所修改,有的子程序中声明为static的数据更有可能给多线程程序带来灾难性的打击,这些正是编写多线程程序时最需要注意的地方。
除了以上所说的优点外,不和进程比较,多线程程序作为一种多任务、并发的工作方式,当然有以下的优点:
1) 提高应用程序响应。这对图形界面的程序尤其有意义,当一个操作耗时很长时,整个系统都会等待这个操作,此时程序不会响应键盘、鼠标、菜单的操作,而使用多线程技术,将耗时长的操作(time consuming)置于一个新的线程,可以避免这种尴尬的情况。
2) 使多CPU系统更加有效。操作系统会保证当线程数不大于CPU数目时,不同的线程运行于不同的CPU上。
3) 改善程序结构。一个既长又复杂的进程可以考虑分为多个线程,成为几个独立或半独立的运行部分,这样的程序会利于理解和修改。
Linux C多线程编程
Linux下的多线程编程需要注意的是程序需要包含头文件pthread.h,在生成可执行文件的时候需要链接库libpthread.a或者libpthread.so。
线程创建函数:
pthread_create(pthread_t *thread, pthread_attr_t * attr, void *(*start_routine)(void *),void *arg);
参数说明:
Thread 标示一个线程,它是一个pthread_t类型的变量(unsigned long int)
attr 用于设置线程的属性,默认是null
start_routine当线程分配资源成功后,线程中所运行的单元,通俗的说就是你自己写的一个函数
Arg线程函数运行时传入的一个参数,一般可以用这个传入的参数去控制线程结束
函数返回值:
创建成功返回0,创建失败返回非0值,常见错误返回代码为EAGAIN何EINVAL,EGAIN标示系统中线程的数量达到上限,错误代码EINVAL表示线程的属性非法。
注意:线程创建城成功后,新创建的线程按照参数3和参数4确定一个运行函数,原来的线程在线程创建函数返回后继续运行下一行代码。
线程结束函数:pthread_join()和pthread_exit()
pthread_join()用来等待一个线程运行结束。这个函数是阻塞函数,一直被等待的线程结束为止,函数才返回并且收回被等待线程的资源。函数的原型为:
Extern int pthread_join_P((pthread_t _th,void **__thread_return));
_th:线程的标示符,也就是线程创建成功的值,在通俗的说就是pthread_create函数运行成功后的第一个参数
__thread_return:返回值,它是一个指针用来存贮被等待线程的返回值。
线程函数的结束方式有两种:一种是线程函数运行结束,不用返回结果;另一种就是通过函数pthread_exit()来实现,将结果传出。
函数原型是:
Extern void pthread_exit_P((void*_retval))
参数是函数的返回值,这个值可以被pthread_join函数捕获,通过__thread_return参数获得此值。
说道线程的创建还有一点必须要提及,那就是线程的属性。一般在我们创建线程的时候设置attr属性的时候都是使用null,这个是默认参数。但是在很多时候需要调整线程的属性,特别是线程优先级。
线程的属性结构为:pthread_attr_t,在头文件
ypedef struct
{
int detachstate; 线程的终止状态
int schedpolicy; 线程调度策略(优先级)
struct sched_param schedparam; 线程的调度参数
int inheritsched; 线程的继承性
int scope; 线程的作用域
size_t guardsize; 线程栈末尾的警戒缓冲区大小
int stackaddr_set; 运行栈
void * stackaddr; 线程栈的位置
size_t stacksize; 线程栈的大小
}pthread_attr_t;
要注意的是线程的属性值不能直接设置,必须要用先关的函数进行操作。线程属性的初始化函数pthread_attr_init(),这个函数必须在pthread_create()函数之前调用。
线程间的互斥:
线程的互斥函数有:互斥函数的初始化pthread_mutex_init(),互斥函数的锁定函数pthread_mutex_lock(),互斥函数的预锁定函数pthread_mutex_trylock(),互斥函数的解锁函数pthread_mutex_unlock(),互斥函数的销毁函数pthread_mutex_destroy()
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#define MUXNUMBER 10
pthread_mutex_t test_mutex;
int testi = 0;
int testis[10 * 1000];
int count=0;
void testfun(void)
{
testis[testi] = testi * 2;
usleep(1000);
testi++;
}
void thread_func()
{
int m_count=0;
while(m_count<1000)
{
pthread_mutex_lock(&test_mutex);
testfun();
pthread_mutex_unlock(&test_mutex);
m_count++;
//sleep(1);
}
}
int main()
{
pthread_t t[10];
pthread_mutex_init(&test_mutex,NULL);
int i;
for(i=0;i<MUXNUMBER;i++)
{
if(pthread_create(&t[i],NULL,(void*)thread_func,NULL) == -1)
{
printf("create Thread error !\n");
exit(1);
}
//sleep(1);
}
for(i=0;i<MUXNUMBER;i++)
{
pthread_join(t[i],NULL);
//sleep(1);
}
pthread_mutex_destroy(&test_mutex);
for(i=0;i<10000;i++)
{
if(testis[i]!=i*2)
{
printf("第%d个数据出错!:%d\n",i,testis[i]);
}
}
return 0;
}
简单说下程序功能:程序创建十个线程,每个线程都调用testfun,通过互斥锁保证数据的正常。