先来看以下关于c语言的几个问题
2.1 void*
C语言中void * 为 “不确定类型指针”,void *可以用来声明指针。如:void * a;
(1)void *可以接受任何类型的赋值:
void *a = NULL;
int * b = NULL;
a = b;//a是void * 型指针,任何类型的指针都可以直接赋值给它,无需进行强制类型转换
(2)void *可以赋值给任何类型的变量 但是需要进行强制转换:
int * a = NULL ;
void * b ;
a = (int *)b;
(3)void* 在转换为其他数据类型时,赋值给void* 的类型 和目标类型必须保持一致。
即void* 类型接受了int * 的赋值后 这个void * 不能转化为其他类型,必须转换为int *类型
2.2 pthread_t
它是POSIX中新线程的标识符,关于POSIX这一篇文章介绍了pthread_attr_t ,线程同步:互斥锁(量)与条件变量,线程取消。
2.3 pthread_create
函数pthread_create用来创建一个线程,它的原型为:
extern int pthread_create __P ((
pthread_t *__thread,
__const pthread_attr_t *__attr,
void (__start_routine) (void *),
void *__arg));
第一个参数为指向线程标识符的指针
第二个参数用来设置线程属性
第三个参数是线程运行函数的起始地址
最后一个参数是运行函数的参数。
这里,我们的函数thread不需要参数,所以最后一个参数设为空指针。第二个参数我们也设为空指针,这样将生成默认属性的线程。对线程属性的设定和修改我们将在下一节阐述。当创建线程成功时,函数返回0,若不为0则说明创建线程失败,常见的错误返回代码为EAGAIN和EINVAL.前者表示系统限制创建新的线程,例如线程数目过多了;后者表示第二个参数代表的线程属性值非法。创建线程成功后,新创建的线程则运行参数三和参数四确定的函数,原来的线程则继续运行下一行代码。
2.4 pthread_join、pthread_exit
函数pthread_join用来等待一个线程的结束。函数原型为:
extern int pthread_join __P ((pthread_t __th, void **__thread_return));
第一个参数为被等待的线程标识符
第二个参数为一个用户定义的指针,它可以用来存储被等待线程的返回值。
这个函数是一个线程阻塞的函数,调用它的函数将一直等待到被等待的线程结束为止,当函数返回时,被等待线程的资源被收回。一个线程的结束有两种途径,一种是我们上面的例子,函数结束了,调用它的线程也就结束了。
另一种方式是通过函数pthread_exit来实现。它的函数原型为:
extern void pthread_exit __P ((void *__retval)) attribute ((noreturn));
唯一的参数是函数的返回代码
只要pthread_join中的第二个参数thread_return不是NULL,这个值将被传递给 thread_return.最后要说明的是,一个线程不能被多个线程等待,否则第一个接收到信号的线程成功返回,其余调用pthread_join的线程则返回错误代码ESRCH。
![](/qrcode.jpg)
互斥锁用来保证一段时间内只有一个线程在执行一段代码。我们继续往下看:
2.5 pthread_mutex_init
函数pthread_mutex_init用来生成一个互斥锁。NULL参数表明使用默认属性。如果需要声明特定属性的互斥锁,须调用函数 pthread_mutexattr_init.函数pthread_mutexattr_setpshared和函数 pthread_mutexattr_settype用来设置互斥锁属性。前一个函数设置属性pshared,它有两个取值, PTHREAD_PROCESS_PRIVATE和PTHREAD_PROCESS_SHARED.前者用来不同进程中的线程同步,后者用于同步本进程的不同线程。在上面的例子中,我们使用的是默认属性PTHREAD_PROCESS_ PRIVATE.后者用来设置互斥锁类型,可选的类型有PTHREAD_MUTEX_NORMAL、PTHREAD_MUTEX_ERRORCHECK、 PTHREAD_MUTEX_RECURSIVE和PTHREAD _MUTEX_DEFAULT.它们分别定义了不同的上所、解锁机制,一般情况下,选用最后一个默认属性。
2.6 pthread_mutex_lock、pthread_mutex_unlock、pthread_delay_np
pthread_mutex_lock声明开始用互斥锁上锁,此后的代码直至调用pthread_mutex_unlock为止,均被上锁,即同一时间只能被一个线程调用执行。当一个线程执行到pthread_mutex_lock处时,如果该锁此时被另一个线程使用,那此线程被阻塞,即程序将等待到另一个线程释放此互斥锁。
上面的内容关于c语言和POSIX
下面的内容来自这篇文章,在前面先讲解了c++的核心基本内容,后面这个案例就是基于c和POSIX的,提出了对象线程的封装
源码如下:
#include<iostream>
#include <pthread.h>
using namespace std;
class Thread //虚基类Thread
{
public:
Thread(string name = "Unknown"){}
virtual ~Thread(){}
void Start()
{
pthread_create(&m_ThreadID, NULL, ThreadFunc, this);
}
/* 第一个参数为指向线程标识符的指针
第二个参数用来设置线程属性
第三个参数是线程运行函数的起始地址,即为线程入口地址
最后一个参数是运行函数的参数,即入口函数的参数
*/
static void* ThreadFunc(void* pth)
{
Thread* p = static_cast<Thread*>(pth);//类型转换,得到类对象
p->Run();
}
virtual void Run() = 0; //Thread类的启动函数为虚函数,说明Thread仅为一个基类,被继承
private:
pthread_t m_ThreadID; //私有变量是一个线程
};
class Test:public Thread //Test类继承自Thread
{
public:
virtual void Run()
{
while(1)
{
cout<<"In Test::Run()"<<endl;
}
}
};
int main()
{
Thread* thread1 = new Test();
thread1->Start();
sleep(1);
}
上面的代码用Thread这个类来实现线程的封装,用户自己要启用的线程类都要继承这个Thread类,在Thread类的Start函数里,调用了pthread_create创建了一个线程,并将ThreadFunc设置为线程函数,把线程的this指针传递给了这个函数,在这个线程函数里调用了
虚函数Run,这个Run函数最终会利用多态调用到用户线程类的Run函数,这就是上面代码的基本原理
2.7 对象线程封装
下面这种线程封装方式是用基于对象的封装方式,用到了boost库中的神器boost::function, boost::bind,这个神器原理就是帮定一个函数对象,函数对象可以包含参数,我们可以利用这个神器调用任意全局函数,甚至类的成员函数,源码如下:
#include<iostream>
#include <pthread.h>
#include <boost/function.hpp>
#include <boost/bind.hpp>
using namespace std;
class Thread
{
typedef boost::function<void(void)> ThreadFunctionCallBack;
public:
Thread(ThreadFunctionCallBack cb, string name = "Unknow")
:m_cb(cb)
{
}
~Thread()
{
}
void Start(void)
{
pthread_create(&m_ThreadID, NULL, ThreadFunction, this);
}
static void* ThreadFunction(void* obj)
{
Thread* thread = static_cast<Thread*>(obj);
thread->m_cb();
}
private:
ThreadFunctionCallBack m_cb;
pthread_t m_ThreadID;
};
class Test
{
public:
void run(void)
{
cout<<"In test::run()"<<endl;
}
};
int main()
{
Test t;
Thread thread1(boost::bind(&Test::run, &t));
thread1.Start();
usleep(100);
}
我们可以看到Test类不需要继承自Thread类,我们直接可以将boost::bind帮定的函数对象传递给Thread的构造函数,这里为了简单我们把this指针传递给了ThreadFunc,我们完全可以传递任何参数给ThreadFunc, 因为我们不需要使用多态,这就是基于对象的思想,比面向对象似乎更直接,使用起来更方便,更简单。