**
Linux中joinable和原子、互斥锁的操作
**
注:线程默认是joinable(结合线程),本次使用的是进程间同步信号量操作。
使用分文件编程思想,定义工厂头文件 “product.h”
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
typedef struct Nodes
{
int num;
struct Nodes *next;
} Nodes;
union semun
{
int value;
};
int sem_Init(int sem_Id, int value);
int sem_Del(int sem_Id);
int sem_P(int sem_Id);
int sem_V(int sem_Id);
~
2.关于信号量PV操作,实现函数如下product.c
#include "product.h"
int sem_Init(int sem_Id, int value)
{
union semun sem_union;
sem_union.value = value;
if((semctl(sem_Id, 0 ,SETVAL, sem_union)) == -1)
{
perror("semctl");
return -1;
}
return 0;
}
int sem_Del(int sem_Id)
{
union semun sem_union;
if((semctl(sem_Id, 0 , IPC_RMID, sem_union))== -1)
{
perror("semdel");
return -1;
}
return 0;
}
int sem_P(int sem_Id)
{
struct sembuf sem_buf;
sem_buf.sem_num = 0;
sem_buf.sem_op =-1;
sem_buf.sem_flg =SEM_UNDO;
if((semop(sem_Id, &sem_buf, 1))==-1)
{
perror("sem_p");
return -1;
}
return 0;
}
int sem_V(int sem_Id)
{
struct sembuf sem_buf;
sem_buf.sem_num = 0;
sem_buf.sem_op = 1;
sem_buf.sem_flg = SEM_UNDO;
if((semop(sem_Id,&sem_buf, 1))==-1)
{
perror("sem_v");
return -1;
}
return 0;
}
3.创建生产者和消费者线程,开启线程。
注:使用链表,手动输入内容作为工厂的“ 实物链 ”,让消费者合理有序“ 消费 ”,退出时无内存泄漏。
#include "product.h"
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
static int sem_Id;
Nodes *head = NULL;
Nodes *createNodes()
{
Nodes *nodes = (Nodes *)malloc(sizeof(Nodes));
nodes->next = NULL;
return nodes;
}
Nodes *addTail()
{
Nodes *tempNode = head;
Nodes *newNode = NULL;
newNode = createNodes();
if (NULL == head)
{
head = newNode;
return head;
}
else
{
while (tempNode->next != NULL)
{
tempNode = tempNode->next;
}
tempNode->next = newNode;
newNode->next = NULL;
return newNode;
}
}
void deleNode(Nodes *newNode)
{
Nodes *temp = NULL;
if (NULL == newNode)
{
return;
}
else if (newNode == head)
{
temp = head->next;
free(newNode);
head = temp;
}
else
{
temp = head->next;
free(head);
}
}
void printids(const char *s)
{
pid_t pid;
pthread_t tid;
pid = getpid();
tid = pthread_self();
printf("%s :pid :%d tid :%d\n", s, (unsigned int)pid, (unsigned int)tid);
}
void printNode(Nodes *head)
{
Nodes *temp = head;
if (NULL == head)
{
printf("head is NULL! \n");
return;
}
else
{
while (temp != NULL)
{
printf("get datas is %d \n", temp->num);
temp = temp->next;
}
}
}
void *thread_producter(void *arg)
{
int i;
int flag;
int x;
Nodes *newNode = head;
printids("start thread_producter!\n");
pthread_mutex_lock(&mutex);
for (i = 0; i < 5; i++)
{
newNode = addTail();
printf("Please input %d number info:\n", i);
x = scanf("%d", &flag);
if (x)
{
newNode->num = flag;
}
else
{
printf("input error!\n");
break;
}
}
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
}
void *thread_con1(void *arg)
{
int i=0;
pthread_mutex_lock(&mutex);
pthread_cond_wait(&cond, &mutex);
printids("thread_consumer_one start!");
pthread_mutex_unlock(&mutex);
sem_V(sem_Id);
//该处放置锁,有两种情况。一种是被第二个消费者拿走,那么我们需要第二个消费拿完放回锁,让这个消费者继续运行,达到指定内容,让第二个消费者进行消费。还有一种情况是在当下继续运行。
sem_P(sem_Id);
while (head != NULL)
{
printf("consumer one get node msg:%d\n", head->num);
deleNode(head);
if(i == 2)
{
sem_V(sem_Id);
}
sleep(1);
i++;
}
}
void *thread_con2(void *arg)
{
sem_P(sem_Id);
printids("thread_consumer_two start!");
sem_V(sem_Id);
sem_P(sem_Id);
while (head != NULL)
{
printf("consumer two get node msg:%d\n", head->num);
deleNode(head);
}
sem_V(sem_Id);
}
int main(int argc, char **argv)
{
int err;
void *ret;
pthread_t thr_joinable_producter;
pthread_t thr_consumer_one;
pthread_t thr_consumer_two;
sem_Id = semget(ftok(".", 1), 1, IPC_CREAT | 0666);
if (0 == sem_Id)
{
printf("semget success! \n");
}
else
{
printf("semget failed:%s\n", strerror(sem_Id));
}
sem_Init(sem_Id, 0);
pthread_mutex_init(&mutex, NULL);
pthread_cond_init(&cond, NULL);
err = pthread_create(&thr_joinable_producter, NULL, thread_producter, NULL);
if (0 != err)
{
printf("pthread_create failed: %s\n", strerror(err));
}
else
{
printf("thr_producter start success!\n");
}
err = pthread_create(&thr_consumer_one, NULL, thread_con1, NULL);
if (0 != err)
{
printf("thr_consumer_one failed:%s\n", strerror(err));
}
else
{
printf("thr_consumer_one success! \n");
}
err = pthread_create(&thr_consumer_two, NULL, thread_con2, NULL);
if (0 != err)
{
printf("thr_consumer_two failed:%s\n", strerror(err));
}
else
{
else
{
printf("thr_consumer_two success! \n");
}
pthread_join(thr_consumer_one, &ret);
pthread_join(thr_consumer_two, &ret);
pthread_join(thr_joinable_producter, &ret);
//注意释放资源,也就是销毁锁。
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);
sem_Del(sem_Id);
return 0;
}
4.Makefile的编写
TARGET=test
CC=gcc
INCLUDE=./
LIBS=-lpthread
OBJS=main.o product.o
${TARGET}:${OBJS}
${CC} -o ${TARGET} ${OBJS} ${LIBS}
main.o:main.c
${CC} -c main.c
product.o:product.c
${CC} -c product.c
.PHONY:clean
clean:
rm -f *.o ${TARGET}
注意事项:前面目标文件以及虚拟引用,后面必须使用 = 不是冒号
TARGET=test
CC=gcc
INCLUDE=./
LIBS=-lpthread
OBJS=main.o product.o
在生成目标文件的指令时,所需要的依赖.0文件必须添加在后面,以及所使用的链接库。
${TARGET}:${OBJS}
${CC} -o ${TARGET} ${OBJS} ${LIBS}
5.实验现象:
6.关于线程
对于可连接的线程而言,它不会自动释放其内存空间,必须对该线程使用 pthread_join()才能释放其内存空间。
对于可分离的线程而言,该线程运行结束后会自动释放所有资源。
可以看出pthread_join()有两种作用:
用于等待其他线程结束:当调用 pthread_join() 时,当前线程会处于阻塞状态,直到被调用的线程结束后,当前线程才会重新开始执行。
对线程的资源进行回收:如果一个线程是非分离的(默认情况下创建的线程都是非分离)并且没有对该线程使用 pthread_join() 的话,该线程结束后并不会释放其内存空间,这会导致该线程变成了“僵尸线程”。