进程:各个进程的内存地址空间相互独立,只能通过请求操作系统内核帮助(CPU要切换到内核态)来完成进程间的通信。切换进程时需要保存\恢复进程进程的运行环境,需要切换内存地址空间开销大。 线程:进程是资源分配的基本单位,同一个进程下的各个线程共享(进程的资源)内存地址空间,可以直接通过读写内存空间进行通信。线程并发开销很小。进程并发开销很大。
引入线程后线程是cpu调度基本单位。
描述: fork()通过复制调用进程创建一个新的进程。子进程是父进程的副本,子进程除了ID与父进程不同,其他都相同。
测试:
[root@oracledb ~]# vim forktest.c
#include <stdio.h>
#include <unistd.h>
int main()
{
fork();
printf("fork test\n");
return 0 ;
}
[root@oracledb ~]# gcc forktest.c
[root@oracledb ~]# ./a.out
fork test
[root@oracledb ~]# fork test
根据返回值判断父子进程,让父子进程做不同的逻辑:
[root@oracledb ~]# vim forktest1.c
#include <stdio.h>
#include <unistd.h>
int main()
{
pid_t pid;
pid=fork();
if (pid>0) //parent
{
printf("parent\n");
}
if (pid==0) //son
{
printf("son\n");
}
if (pid<0)
{
printf("error\n");
}
printf("fork test\n");
return 0 ;
}
[root@oracledb ~]# gcc forktest1.c
[root@oracledb ~]# ./a.out
parent
fork test
[root@oracledb ~]# son
fork test
程序执行完后,命令行提示符没有正常,是因为子进程结束后,子进程占用的资源没有被及时回收,成为僵尸进程,被init进程回收后恢复正常。可以在父进程添加wait函数等待子进程退出,父进程退出前先处理子进程。
[root@oracledb ~]# cp forktest1.c forktest2.c
#include <stdio.h>
#include <unistd.h>
int main()
{
pid_t pid;
pid=fork();
if (pid>0) //parent
{
printf("parent\n");
wait(NULL);
}
if (pid==0) //son
{
printf("son\n");
}
if (pid<0)
{
printf("error\n");
}
printf("fork test\n");
return 0 ;
}
[root@oracledb ~]# gcc forktest2.c
[root@oracledb ~]# ./a.out
parent
son
fork test
fork test
功能: exec()函数家族用新进程替换当前进程,并从新进程的main入口开始执行,无返回值,除非exec()发生错误返回-1。
example:
[root@oracledb ~]# vim exectest.c
#include <stdio.h>
#include <unistd.h>
int main()
{
pid_t pid;
pid=fork();
if (pid>0) //parent
{
printf("parent\n");
wait(NULL);
}
if (pid==0) //son
{
execlp("/bin/touch","touch","aaaas.txt",NULL);
printf("son\n");
}
if (pid<0)
{
printf("error\n");
}
}
[root@oracledb ~]# gcc exectest.c
[root@oracledb ~]# ./a.out
parent
[root@oracledb ~]# ls aaaas.txt
aaaas.txt
多线程并发访问同一资源,可能导致数据异常。通过互斥锁确保共享资源每次只能被一个线程访问。
例如商品剩余5件:
pthread_create参数: (1)pthread_t thread:事先创建好的pthread_t类型的参数。成功时tidp指向的内存单元被设置为新创建线程的线程ID。 (2)const pthread_attr_t attr:用于定制各种不同的线程属性。通常直接设为NULL。 (3)void (start_routine) (void ):新创建线程从此函数开始运行。 (4)void arg:函数的参数。无参数时设为NULL即可。有参数时输入参数的地址。当多于一个参数时应当使用结构体传入
pthread_join: 用于等待一个线程结束。
[root@oracledb ~]# vim threadtest.c
#include <stdio.h>
#include <pthread.h>
#define M 3
static tickets=5;
void * fun(void * args)
{
while(tickets>0)
{
sleep(1);
tickets--;
printf("tickets:%d\n",tickets);
}
return NULL;
}
int main()
{
pthread_t id[M];
int i;
for(i=0;i<M;i++)
pthread_create(&id[i],NULL,fun,NULL);
for(i=0;i<M;i++)
pthread_join(id[i],NULL);
}
[root@oracledb ~]# gcc threadtest.c -pthread -o testpthread
[root@oracledb ~]# ./testpthread
tickets:4
tickets:3
tickets:2
tickets:1
tickets:0
tickets:-1
tickets:-2
互斥锁使用:
[root@oracledb ~]# cp threadtest.c threadtest1.c
[root@oracledb ~]# vim threadtest1.c
#include <stdio.h>
#include <pthread.h>
#define M 3
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
static tickets=5;
void * fun(void * args)
{
pthread_mutex_lock(&mutex);
while(tickets>0)
{
sleep(1);
tickets--;
printf("tickets:%d\n",tickets);
}
pthread_mutex_unlock(&mutex);
return NULL;
}
int main()
{
pthread_t id[M];
int i;
for(i=0;i<M;i++)
pthread_create(&id[i],NULL,fun,NULL);
for(i=0;i<M;i++)
pthread_join(id[i],NULL);
}
[root@oracledb ~]# gcc threadtest1.c -pthread -o testpthread
[root@oracledb ~]# ./testpthread
tickets:4
tickets:3
tickets:2
tickets:1
tickets:0
互斥锁多线程:一个线程访问加锁的操作区域时,如果已经被其他线程加锁,则线程切换到其他线程执行。损耗上下文切换的性能。 自旋锁多线程:一个线程访问加锁的操作区域时,如果已经被其他线程加锁,则线程一直轮训访问锁,不切换线程。(nginx)