本文使用pthread的库,调用其中API可方便实现多线程效果。
本文主要讲解实际中用到的几个接口函数,足够一般开发使用。
实例为应用与UDP协议的收发,打开接收数据线程,接收不影响主线程,主线程进行数据处理等工作,方便二次开发。
关于UDP的教程可以参看:http://blog.csdn.net/weixin_37895339/article/details/72716774
首先介绍需要用到的接口函数。
1.pthread_create()函数
用于创建线程,调用该函数后,线程开始执行。创建成功时返回0。
int pthread_create(pthread_t *thread, const pthread_attr_t, void *(*_function)(void*), void *arg)
参数介绍:
a.第一个参数为线程id的指针,创建方式为:
pthread_t thread[Thread_Num];
将id的地址付给第一个参数:
rc = pthread_create(*thread[i],NULL,NULL,NULL);
b.第二个参数为线程属性设置,创建方式为:
pthread_attr_t attr; //线程属性设置
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_JOINABLE); //将属性设置为joinable,可以使主线程等待该线程执行完后,再结束主线程
rc = pthread_create(*thread[i],&attr,NULL,NULL);
c.第三个参数为线程执行的函数&第四个参数为该函数需要的参数
该函数类型为 void*,即返回值为void*。参数类型为void*,如果要传递多个参数,可以建立一个结构体进行传递。示例如下:
struct thread_recdata{ //函数所需传递的参数,一共有两个,放在一个结构中
int sockfd; //socket
char *info; //接受的数据
};
thread_recdata thread_rec; //创建函数所用参数
void *recinfo(void *rec_data); //创建函数
rc = pthread_create(&thread[0], &attr ,recinfo,(void *)&thread_rec); //开启线程
pthread_attr_destroy(&attr); //释放属性attr的空间
2.pthread_join()函数
调用该函数可以阻塞主线程,等待子线程运行完后再结束主线程。如果不阻塞,可能出现主线程运行完,程序结束,但子线程还没运行完的情况。
int pthread_join(pthread_t thread,void **status_value)
d第一个参数为要等待子线程的id,第二个参数为返回的线程状态信息,其类型为void*。实例为:
void *status;
rc = pthread_join(thread[i], &status);
if (rc){
printf("Error:unable to join");
exit(-1);
}
3.线程锁
当两个线程同时调用修改同一个数据变量时,必须加线程锁,否则将会出现数据错乱的情况。数据加锁后,其余调用该变量的线程将会被阻塞,直到释放锁。实例如下:
pthread_mutex_t rec_mutex; //创建线程锁
pthread_mutex_lock(&rec_mutex); //加锁
rec_queue.push(thread_info->info); //修改数据
pthread_mutex_unlock(&rec_mutex); //解锁
下面介绍一个应用于UDP协议的多线程实例:
本例打开一个接受线程不断接受外部发来的数据,主线程处理这些数据,另开一个显示线程用作调试。方便二次开发。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define port_in 12321
#define port_out 12322
#define Num_Thread 3
char info_rec[10][128];
pthread_mutex_t rec_mutex; //线程锁
struct thread_senddata{
int sockfd; //socket
char *info; //发送的数据
char *addr; //发送的地址
int port; //发送的端口
};
struct thread_recdata{
int sockfd; //socket
char *info; //接受的数据
};
int initudp(void)
{
int sockfd;
/* Create Socket*/
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if(-1==sockfd){
return 0;
puts("Failed to create socket");
}
/*Config Socket Addr*/
struct sockaddr_in addr;
socklen_t addr_len=sizeof(addr);
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET; // Use IPV4
addr.sin_port = htons(port_in); //
addr.sin_addr.s_addr = inet_addr("10.106.2.146");
/* Time out*/
// struct timeval tv;
// tv.tv_sec = 0;
// tv.tv_usec = 200000; // 200 ms
// setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(struct timeval));
/* Bind Socket*/
if (bind(sockfd, (struct sockaddr*)&addr, addr_len) == -1){ //收数据才需要bind
printf("Failed to bind socket on port %d\n", port_in);
close(sockfd);
return false;
}
return sockfd;
}
void sendinfo(thread_senddata *thread_info) //发送数据调用
{
int len;
struct sockaddr_in dest;
socklen_t dest_len = sizeof(dest);
memset(&dest, 0, sizeof(dest));
dest.sin_family = AF_INET;
dest.sin_port = htons(thread_info->port);
dest.sin_addr.s_addr = inet_addr(thread_info->addr);
if(strlen(thread_info->info)!=0) //如果数组有数则发送
{
len = strlen(thread_info->info);
sendto(thread_info->sockfd, thread_info->info, len, 0, (sockaddr*)&dest, dest_len);
}
}
void *recinfo(void *rec_data) //接受线程调用的函数
{
struct thread_recdata *thread_info;
thread_info = (struct thread_recdata *)rec_data;
struct sockaddr_in src;
socklen_t src_len = sizeof(src);
memset(&src, 0, sizeof(src));
while(1)
{
int sz = recvfrom(thread_info->sockfd, thread_info->info, 128, 0, (sockaddr*)&src, &src_len);
if (sz > 0){
thread_info->info[sz] = 0;
pthread_mutex_lock(&rec_mutex);
for(int j =0 ; j < 10; j++)//最多存10个指令,不够后面再修改吧
{
if(strlen(info_rec[j])==0) //给空的地方存
{
strcpy(info_rec[j],thread_info->info);
break;
}
}
pthread_mutex_unlock(&rec_mutex);
}
}
}
void *get_info_rec(void *arg) //显示线程调用的函数
{
while(1)
{
pthread_mutex_lock(&rec_mutex);
if(strlen(info_rec[0])!=0)
{
printf("%s \n",info_rec[0]);
for(int i=0;i<9;i++)
{
strcpy(info_rec[i],info_rec[i+1]);
}
memset(info_rec[9],0,128);
}
pthread_mutex_unlock(&rec_mutex);
//sleep(1);
}
}
int main(void)
{
char info[100]; //发送的
char addr[20]; //发送目标地址
struct thread_senddata thread_info; //发送信息函数利用的信息结构体
char buffer[128]; //接受到的数据,要不要其实无所谓了,后面再修改吧
int sockfd;
thread_recdata thread_rec;
memset(info_rec,0,10*128);
/*线程所需变量*/
pthread_t thread[Num_Thread]; //存储线程的id
pthread_attr_t attr; //线程属性设置
int rc; //创建线程的返回值,检查是否创建成功
void *status;
/*创建UDP*/
sockfd = initudp();
if (sockfd==0)
{
printf("socket error!");
}
/*设置接受线程参数*/
thread_rec.sockfd = sockfd;
thread_rec.info = buffer;
/*创建线程*/
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_JOINABLE);
rc = pthread_create(&thread[0], &attr ,recinfo,(void *)&thread_rec); //收数据
if (rc){
printf("Error:unable to create thread");
exit(-1);
}
rc = pthread_create(&thread[1], &attr ,get_info_rec,NULL); //显示数据
if (rc){
printf("Error:unable to create thread");
exit(-1);
}
/*设置发送的数据*/
strcpy(info,"$HAKQGS112233");
// strcpy(addr,"10.106.3.65");
strcpy(addr,"10.106.2.146");
thread_info.addr = addr;
thread_info.info = info;
thread_info.port = port_out;
thread_info.sockfd = sockfd;
while(1)
{
sendinfo(&thread_info);
sleep(1);
strcpy(info,"$HAYRZT030000");
sendinfo(&thread_info);
sleep(1);
strcpy(info,"$HAYRZT030001");
sendinfo(&thread_info);
sleep(1);
strcpy(info,"$HAYRZT030002");
sendinfo(&thread_info);
}
//删除属性,并等待其他线程
pthread_attr_destroy(&attr);
for(int i = 0 ;i < Num_Thread;i++)
{
rc = pthread_join(thread[i], &status);
if (rc){
printf("Error:unable to join");
exit(-1);
}
}
pthread_exit(NULL); //删除线程
return 1;
}
WALDM