1.前台进程和后台进程
当用户通过xshell等工具登录云服务器时,linux系统会为用户创建一个会话(session)。每个会话会默认创建一个bash命令行解释器进程,它初始是前台进程,负责接收键盘输入、执行命令并返回结果。

这个bash进程现在也就是前台进程,前台进程只能有一个,后台进程可以有很多个。
前台进程与后台进程的区别
先看代码:创建代码,每隔一秒打印hello world字符串。
#include <iostream>
#include <unistd.h>
using namespace std;
int main()
{
while(true)
{
cout<<"hello world"<<endl;
sleep(1);
}
return 0;
}运行结果:

可以发现当我们运行进程后,输入ls,pwd这些指令bash命令行进程都不会执行了,因为这个时候着进程就是一个前台进程了,bash进程已经被切换到后台进程了,这个时候是进程在获取键盘输入,而不是bash进程,所以bash进程也就是不能执行对应的指令了。
前台进程:能获取键盘输入(标准输入),一个会话中只能有一个前台进程。
后台进程:无法获取键盘输入,但可以向显示器输出结果(标准输出/标准错误),一个会话中可以同时存在多个后台进程。注意:前台进程拥有键盘输入权限,后台进程没有,但两者都能向显示器输出内容。
进程切换逻辑

当运行一个程序(如 ./process )时,该程序会成为前台进程,原本的bash会被切换到后台。此时bash无法接收键盘输入,也就无法执行新命令;若用 ctrl+c 终止前台进程,bash会自动回到前台。
进入后台运行程序
在命令末尾加上 & ,例如 ./process & ,可以让程序以后台进程运行,此时bash仍保持为前台进程,可继续执行其他命令。
使用 & 可以让程序后台运行,不影响bash接收新命令。

当我们将test进程变为后台进程后,因为打印是向显示器上面打印,所以会一直打印"hello world"字符串,但是输入指令还是会被执行,说明这个时候bash是前台进程,test为后台进程。
前台进程和后台进程数量
因为前台进程和后台进程都可以向显示器打印数据,所以,我们将后台进程打印的数据重定向到了一个文件中

查看后台进程数量:
ps ajx|head -1&&ps ajx|grep test|grep -v grep

可以看到后台进程被创建了多个,一个会话中前台进程只能有1个,后台进程可以有多个。
后台进程的操作
当我们运行一个后台进程的时候,可以看到会出现一个关于后台进程的信息。

第一个数字表示的是后台进程的任务号,第二个表示的是进程的pid。
查看后台进程的状态
jobs
使用jobs可以查看后台进程的状态

第一列就是任务号,后面就是运行状态了,running表示正在运行。
切换进程状态
fg+任务号
fg加任务号可以切换该任务号的进程为前台进程。
输入fg 3就将后台进程切换到了前台,这个时候bash为后台进程就不能收到我们的指令了。

暂停进程和终止进程
暂停进程
可以使用fg+任务号来时后台进程切换到前台进程,输入ctrl+z来暂停进程,ctrl+z是向进程发送了20号信号。

使用ctrl+z暂停进程后,在查看进程状态,就可以看到后台进程被暂停了,bash也就自动切换回到前台进程了。
终止进程
和暂停进程的方法一样的,通过fg切换到前台进程,使用ctrl+c来终止进程

恢复暂停的后台进程
bg+任务号//恢复后台进程
bg+任务号恢复暂停的后台进程

2.linux的进程关系
向创建一个test后台进程,然后创建3个sleep后台进程
sleep 1000|sleep 2000|sleep 3000
那么三个sleep之间是采用两个管道连接起来的,本质上还是创建了3个sleep进程,然后第一个sleep进程休眠结束后,结果是什么都没有,然后将这个结果通过管道交给第二个sleep进程,然后第二个sleep进程开始休眠,同理结束后通过管道交给第三个sleep进程,所以这三个sleep进程合起来在完成同一个任务,process这个后台进程自己在完成一个任务
所以我们使用如下指令查找一行运行的进程中,带有sleep或者process的进程,grep -e ‘内容|内容’ 是进行正则匹配对应的内容
ps ajx|head -1&&ps ajx|grep -e 'test|sleep'|grep -v grep

先解释各字段的意思:
ppid 父进程 pid。这些进程都由 shell(bash)创建,所以 ppid 就是 bash 的 pid。
pid 进程自身的唯一 id。
pgid 进程组 id。- 3 个 属于同一进程组,pgid 等于第一个 的 pid(它是组长)。- 单独成组,pgid 就是它自己的 pid。
sid 会话 id,等于当前登录 shell(bash)的 pid。同一终端下的所有进程共享同一个 sid。
tty 关联的终端设备。
stat 进程状态(如 休眠、 运行等)。
进程组和任务的关系
一个进程组也就是完成的一个任务,当我们启动了两次test进程,也就是代表着我们启动了两个不同的任务,当我们通过管道创建的3个sleep进程也就是属于一个任务组,所以3个sleep的pgid是系统的,也就意味着他们属于同一个任务组。
同时属于同一个进程组的进程,他们的pgid进程组id会等于进程组中的第一个进程的pid,上图中第一个sleep是第一个启动的进程,所以他也就相当于这个进程组的队长,进程组id就保持和队长的pid一样。
session会话id对于bash的pid
当我们打开xshell去连接云服务器时,登录成功后会fork出一个bash进程,bash进程会使用
setsid()来创建一个会话,那么bash进程也就是会话的首进程。
内核规定:
调用 setsid() 的进程 = 会话首进程
会话首进程的 pid = 整个会话的 sid
所以bash的pid会对于会话id
3.守护进程
后台进程会受到用户的登录和退出的影响的,当我们关闭xshell,在打开时,后台进程就被清理了。
如果我们想要一个后台进程不受用户退出和登录的影响,该怎么办?
守护进程

当张三登录 linux,李四也登录 linux,系统会给每个人单独开一个房间:张三的房间 = 张三的 session,李四的房间 = 李四的 sessio,这里的房间也就是一个会话,两个会话互不打扰,张三 退出,就会关闭这个会话,将这个会话的所有进程终止。
但是李四的会话是不受影响的。我们把一个进程自成一个会话叫做守护进程,给一个进程单独开一个会话,是它不会受其他会话的影响。
linux操作系统中的可能有很多个用户在使用,所以操作系统就会为这些用户每一个用户创建一个对应的session会话,所以系统中就会同时存在多个session会话,那么linux操作系统要不要将这些session会话管理起来?要,那么如何进行管理呢?
先描述再组织,先使用struct结构体将session会话描述起来,然后采用一定的数据结构,例如链表,将这些struct结构体实例化的session会话的描述对象组织起来,从此以后对session会话的管理就转化成了对链表的增删查改
所以系统中一定有对应的系统调用可以创建session会话,所以这也就使得让服务器成为守护进程有了可能
4.tcp服务器与客户端通信(守护进程版)

setsid创建一个会话。
dame.hpp
#include <iostream>
#include <string>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
using namespace std;
const string nullfile="/dev/null";
void daemon(const string&cwd="")
{
//忽略其它信号
signal(sigchld, sig_ign);
signal(sigpipe, sig_ign);
signal(sigstop, sig_ign);
//创建子进程,然后终止父进程,子进程会拷贝父进程资源,例如文件描述符,页表,地址空间,代码等,
//然后由子进程继续向后执行代码
if(fork() > 0)
exit(0);
//创建session会话,子进程成为服务器,然后守护进程化
setsid();
//更改当前子进程代表的服务器进程的工作目录
if(!cwd.empty())
chdir(cwd.c_str());
//打开 /dev/null 文件
int fd = open(nullfile.c_str(), o_wronly);
//重定向 标准输入,标准输出,标准错误
if(fd > 0)
{
dup2(fd, 0);
dup2(fd, 1);
dup2(fd, 2);
close(fd);
}
}但是不能直接调用 setsid,因为进程组组长 不能调用 setsid 创建新会话,只有组员才能调用。
但是服务器一运行:自己就是一个进程组,自己就是组长,直接调用 setsid 会失败。所以使用fork 创建子进程,父进程直接退出,由子进程来创建会话,子进程彻底脱离原来的终端,自成会话。
父进程死了,子进程还活着,子进程变成 孤儿进程被系统 1 号进程领养
所以守护进程本质就是孤儿进程 + 自成会话。
因为守护进程已经:脱离终端,不跟用户交互,所以对于标准输入 0是没人输入,标准输出 1 是 没人看,标准错误 2是没人看,这些文件描述符留着占位置、可能出问题。
所以可以打开 /dev/null (黑洞文件,写进去就消失),然后用 dup2 ,把 0、1、2 全部重定向到 /dev/null
守护进程工作目录最好切换到根目录 /,避免:占用某个目录导致无法卸载,日志、文件路径混乱
所以可以使用chdir改变目录
以前日志需要打印到显示器,但是现在标准输入,输出,错误都被关闭了 ,但是我们又希望看到日志打印的信息
所以可以在将日志系统根据不同的错误等级往对应的里写文件,以后看日志直接去文件中看
同时我们希望守护进程不想被随便干扰:
用 signal 忽略 sigpipe (管道破裂)忽略 sigchld (子进程退出)忽略 sigtstp (ctrl+z)忽略 sighup (挂断)
main.cpp
改变日志打印方式,根据日志等级王不同的文件中打印
#include <iostream>
#include <memory>
#include "server.hpp"
void usage(const std::string str)
{
std::cout << "\n\tusage: " << str << " port[1024+]\n" << std::endl;
}
int main(int argc, char* argv[])
{
if(argc != 2)
{
usage(argv[0]);
exit(usageerror);
}
lg.enable(classfile);
uint16_t port = std::stoi(argv[1]);
std::unique_ptr<tcpserver> server(new tcpserver(port));
server->init();
server->startserver();
return 0;
}server.hpp
启动进程守护
void startserver()
{
daemon();
threadpool<task>::getinstance()->start();
lg(info,"tcpserver is running");
for(;;)
{
struct sockaddr_in client;
socklen_t len=sizeof(client);
//阻塞等待
int sockfd=accept(listensock_,(struct sockaddr*)(&client),&len);
if(sockfd<0)
{
lg(warning,"accept error,errno:%d,errstring:%s",errno,strerror(errno));
continue;
}
uint16_t clientport=ntohs(client.sin_port);
string clientip=inet_ntoa(client.sin_addr);
lg(info,"get a new link...,clientip:%s,clientport:%d",clientip.c_str(),clientport);
//多进程
// pid_t id=fork();
// if(id==0)
// {
// //子进程
// close(listensock_);
// //子进程创建孙进程后直接退出
// if(fork()>0) exit(0);
// //孙进程
// service(sockfd,clientip,clientport);
// close(sockfd);
// }
// close(sockfd);
// pid_t rid=waitpid(id,nullptr,0);
//多线程
// threaddata*td=new threaddata(sockfd,clientip,clientport,this);
// pthread_t id;
// pthread_create(&id,nullptr,routine,td);
//线程池
task t(sockfd,clientip,clientport);
threadpool<task>::getinstance()->push(t);
}
}删除守护进程,查看进程pid,kill指令删除即可。
测试:

bash还是可以接收到指令。

客户端也可以正常运行。
源代码
client.cpp
#include <iostream>
#include <string>
#include <unistd.h>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
using namespace std;
void usage(const string& str)
{
cout << "\n\tusage: " << str << " serverip serverport" << endl;
}
// 客户端启动格式:./tcpclient serverip serverport
int main(int argc, char* argv[])
{
if(argc != 3)
{
usage(argv[0]);
return 0;
}
string serverip = argv[1];
uint16_t serverport = stoi(argv[2]);
struct sockaddr_in server;
server.sin_family=af_inet;
server.sin_port=htons(serverport);
server.sin_addr.s_addr=inet_addr(serverip.c_str());
socklen_t len=sizeof(server);
while(true)
{
int sockfd = socket(af_inet, sock_stream, 0);
if(sockfd < 0)
{
cerr << "socket create err" << endl;
return 1;
}
bool isconnect=false;
int cnt=5;
do
{
int n=connect(sockfd,(struct sockaddr*)(&server),len);
if(n<0)
{
//连接失败
isconnect=true;
cnt--;
sleep(2);
cerr<<"connect error......,reconnect: "<<cnt<<endl;
}
//连接成功,退出循环
else
{
break;
}
}while(cnt&&isconnect);
if(cnt==0)
{
cerr<<"user offline..."<<endl;
return 2;
}
//客户端发起connect请求时,操作系统会自动进行端口号的随机bind绑定
string message;
char inbuffer[4096];
cout << "please enter# ";
getline(cin, message);
int n = write(sockfd, message.c_str(), message.size());
if(n < 0)
{
cerr << "write err" << endl;
break;
}
n = read(sockfd, inbuffer, sizeof(inbuffer) - 1);
if(n > 0)
{
inbuffer[n] = 0;
cout << inbuffer << endl;
}
//当读取返回值为0时,不要直接break退出
// else
// {
// break;
// }
close(sockfd);
}
return 0;
}daemon.hpp
#include <iostream>
#include <string>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
using namespace std;
const string nullfile="/dev/null";
void daemon(const string&cwd="")
{
//忽略其它信号
signal(sigchld, sig_ign);
signal(sigpipe, sig_ign);
signal(sigstop, sig_ign);
//创建子进程,然后终止父进程,子进程会拷贝父进程资源,例如文件描述符,页表,地址空间,代码等,
//然后由子进程继续向后执行代码
if(fork() > 0)
exit(0);
//创建session会话,子进程成为服务器,然后守护进程化
setsid();
//更改当前子进程代表的服务器进程的工作目录
if(!cwd.empty())
chdir(cwd.c_str());
//打开 /dev/null 文件
int fd = open(nullfile.c_str(), o_wronly);
//重定向 标准输入,标准输出,标准错误
if(fd > 0)
{
dup2(fd, 0);
dup2(fd, 1);
dup2(fd, 2);
close(fd);
}
}dict.txt
apple:苹果 banana:香蕉 book:书 cat:猫 dog:狗 egg:鸡蛋 fish:鱼 girl:女孩 house:房子 ice:冰 juice:果汁 king:国王 lion:狮子 milk:牛奶 nest:鸟巢 orange:橙子 pen:钢笔 queen:女王 rice:米饭 sun:太阳 tree:树 umbrella:雨伞 van:货车 water:水 x-ray:x光 yellow:黄色 zoo:动物园 baby:婴儿 cake:蛋糕 door:门 ear:耳朵 face:脸 grass:草 hand:手 ice cream:冰淇淋 jacket:夹克 kite:风筝 lamp:灯 moon:月亮 nose:鼻子 one:一 pig:猪 queen bee:蜂王 red:红色 school:学校 toy:玩具 uncle:叔叔 vest:背心 window:窗户 box:盒子 car:汽车 dad:爸爸 eye:眼睛 fan:风扇 game:游戏 hat:帽子 iceberg:冰山 jeep:吉普车 key:钥匙 lion cub:幼狮 map:地图 night:夜晚 octopus:章鱼 pear:梨 queen size:大号 rain:雨 star:星星 table:桌子 umbrella stand:伞架 violin:小提琴 wolf:狼 apple pie:苹果派 ball:球 chair:椅子 doll:玩偶 elephant:大象 flower:花 goat:山羊 hen:母鸡 insect:昆虫 jar:罐子 kangaroo:袋鼠 leaf:叶子 monkey:猴子 nest egg:储备金 orange juice:橙汁 pencil:铅笔 queen ant:蚁后 rabbit:兔子 ship:船 tiger:老虎 umbrella hat:伞帽 van driver:货车司机 watch:手表 xylophone:木琴 yacht:游艇 zebra:斑马 air:空气 big:大的 cup:杯子
init.hpp
#pragma once
#include<iostream>
#include<string>
#include<fstream>
#include<unordered_map>
#include"log.hpp"
using namespace std;
const string dictname="./dict.txt";
const string sep=":";
bool solit(string &s,string *part1,string*part2)
{
auto pos=s.find(sep);
if(pos==string::npos) return false;//npos就是无符号整型的最大值,string的findsize_t,算法头文件下的npos返回的则是迭代器。
*part1=s.substr(0,pos);//substr左闭右开
*part2=s.substr(pos+1);
return true;
}
class init
{
public:
init()
{
//ofstream从文件写入
ifstream in(dictname);//从指定文件中读取
if(!in.is_open())
{
lg(fatal,"ifstream open %s error",dictname.c_str());
exit(1);
}
string line;
while(getline(in,line))//不断的从in中读取到line中
{
string part1,part2;
solit(line,&part1,&part2);
dict.insert({part1,part2});
}
in.close();
}
string translation(const string&key)
{
auto iter=dict.find(key);
if(iter==dict.end()) return "unknow";
else return iter->second;
}
private:
unordered_map<string,string> dict;
};main.cpp
#include <iostream>
#include <memory>
#include "server.hpp"
void usage(const std::string str)
{
std::cout << "\n\tusage: " << str << " port[1024+]\n" << std::endl;
}
int main(int argc, char* argv[])
{
if(argc != 2)
{
usage(argv[0]);
exit(usageerror);
}
lg.enable(classfile);
uint16_t port = std::stoi(argv[1]);
std::unique_ptr<tcpserver> server(new tcpserver(port));
server->init();
server->startserver();
return 0;
}makefile
.phony:all all:tcpserver client tcpserver:main.cpp g++ -o $@ $^ -std=c++11 -lpthread -g client:client.cpp g++ -o $@ $^ -std=c++11 -g .phony:clean clean: rm -f tcpserver client
server.hpp
#include <iostream>
#include <string>
#include <cstring>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "log.hpp"
#include "task.hpp"
#include "threadpool.hpp"
#include "daemon.hpp"
extern log lg;
using namespace std;
const int backlog=10;
const string ip="0.0.0.0";
const uint16_t port=8080;
enum
{
usageerror=1,
socketerror,
binderror,
listenerror
};
class tcpserver;
struct threaddata
{
public:
threaddata(int fd,const string&ip,const uint16_t&p,tcpserver*t)
:sockfd(fd)
,clientip(ip)
,clientport(p)
,tsvr(t)
{}
public:
int sockfd;
string clientip;
uint16_t clientport;
tcpserver*tsvr;
};
class tcpserver
{
public:
tcpserver(uint16_t defaultport=port)
:ip_(ip)
,port_(port)
{}
void init()
{
listensock_=socket(af_inet,sock_stream,0);
if(listensock_<0)
{
lg(fatal,"create socket error,errno:%d,errstring:%s",errno,strerror(errno));
exit(socketerror);
}
lg(info,"socked create success,listensock_:%d",listensock_);
//防止偶发性服务器无法重启
int opt=1;
setsockopt(listensock_,sol_socket,so_reuseaddr|so_reuseport,&opt,sizeof(opt));
struct sockaddr_in server;
memset(&server,0,sizeof(server));
server.sin_family=af_inet;
server.sin_port=htons(port_);
server.sin_addr.s_addr=inet_addr(ip_.c_str());
socklen_t len=sizeof(server);
if(bind(listensock_,(struct sockaddr*)(&server),len)<0)
{
lg(fatal,"bind socket error,errno:%d,errstring:%s",errno,strerror(errno));
exit(binderror);
}
lg(info,"socked bind success");
if(listen(listensock_,backlog)<0)
{
lg(fatal,"listen error,errno:%d,errstring:%s",errno,strerror(errno));
exit(listenerror);
}
lg(info,"listen success");
}
void service(int sockfd,string clientip,uint16_t clientport)
{
while(true)
{
char buffer[4096];
ssize_t n=read(sockfd,buffer,sizeof(buffer)-1);
if(n>0)
{
buffer[n]=0;
cout<<"client say#"<<buffer<<endl;
string server_echo="server say#";
server_echo+=buffer;
write(sockfd,server_echo.c_str(),server_echo.size());
}
else if(n==0)
{
//客户端断开连接
lg(info,"%s:%d quit ,server close sockfd:%d",clientip,clientport,sockfd);
break;
}
else
{
//异常情况
lg(info,"read error,%s:%d quit,server close sockfd:%d",clientip,clientport,sockfd);
break;
}
}
}
static void*routine(void*args)
{
pthread_detach(pthread_self());
threaddata*td=static_cast<threaddata*>(args);
td->tsvr->service(td->sockfd,td->clientip,td->clientport);
close(td->sockfd);
delete td;
return nullptr;
}
void startserver()
{
daemon();
threadpool<task>::getinstance()->start();
lg(info,"tcpserver is running");
for(;;)
{
struct sockaddr_in client;
socklen_t len=sizeof(client);
//阻塞等待
int sockfd=accept(listensock_,(struct sockaddr*)(&client),&len);
if(sockfd<0)
{
lg(warning,"accept error,errno:%d,errstring:%s",errno,strerror(errno));
continue;
}
uint16_t clientport=ntohs(client.sin_port);
string clientip=inet_ntoa(client.sin_addr);
lg(info,"get a new link...,clientip:%s,clientport:%d",clientip.c_str(),clientport);
//多进程
// pid_t id=fork();
// if(id==0)
// {
// //子进程
// close(listensock_);
// //子进程创建孙进程后直接退出
// if(fork()>0) exit(0);
// //孙进程
// service(sockfd,clientip,clientport);
// close(sockfd);
// }
// close(sockfd);
// pid_t rid=waitpid(id,nullptr,0);
//多线程
// threaddata*td=new threaddata(sockfd,clientip,clientport,this);
// pthread_t id;
// pthread_create(&id,nullptr,routine,td);
//线程池
task t(sockfd,clientip,clientport);
threadpool<task>::getinstance()->push(t);
}
}
private:
int listensock_;
string ip_;
uint16_t port_;
};task.hpp
#pragma once
#include <iostream>
#include <string>
#include <unistd.h>
#include "log.hpp"
#include "init.hpp"
using namespace std;
extern log lg;
class task
{
public:
task(int sockfd, const string &clientip, const uint16_t clientport)
: sockfd_(sockfd), clientip_(clientip), clientport_(clientport)
{
}
// void run()//普通转发
// {
// char buffer[4096];
// int n=read(sockfd_,buffer,sizeof(buffer)-1);
// if(n>0)
// {
// buffer[n]=0;
// cout<<"client say#"<<buffer<<endl;
// string echo_string="server say#";
// echo_string+=buffer;
// std::cout << echo_string << std::endl;
// write(sockfd_,echo_string.c_str(),echo_string.size());
// }
// else if (n == 0)
// {
// lg(info, "%s:%d quit,server close sockfd:%d", clientip_.c_str(), clientport_, sockfd_);
// }
// else
// {
// lg(warning, "read error,sockfd:%d,clientip:%s,clientport:%d", sockfd_, clientip_.c_str(), clientport_);
// }
// close(sockfd_);
// }
void run()
{
char buffer[4096];
int n=read(sockfd_,buffer,sizeof(buffer)-1);
if(n>0)
{
buffer[n]=0;
cout<<"client say#"<<buffer<<endl;
string echo_string="server say#";
echo_string+=it.translation(buffer);
write(sockfd_,echo_string.c_str(),echo_string.size());
}
else if (n == 0)
{
lg(info, "%s:%d quit,server close sockfd:%d", clientip_.c_str(), clientport_, sockfd_);
}
else
{
lg(warning, "read error,sockfd:%d,clientip:%s,clientport:%d", sockfd_, clientip_.c_str(), clientport_);
}
close(sockfd_);
}
void operator()()
{
run();
}
~task()
{
}
private:
int sockfd_;
string clientip_;
uint16_t clientport_;
init it;
};threadpool.hpp
#include <iostream>
#include <vector>
#include <pthread.h>
#include <string>
#include <unistd.h>
#include <queue>
using namespace std;
struct threadinfo
{
pthread_t tid;
string name;
};
static const int defaultnum = 5;
template <class t>
class threadpool
{
public:
void lock()
{
pthread_mutex_lock(&mutex_);
}
void unlock()
{
pthread_mutex_unlock(&mutex_);
}
void wakeup()
{
pthread_cond_signal(&cond_);
}
void threadsleep()
{
pthread_cond_wait(&cond_, &mutex_);
}
bool isqueueempty()
{
return tasks_.empty();
}
string getthreadname(pthread_t tid)
{
for (auto &t : threads_)
{
if (t.tid == tid)
{
return t.name;
}
}
return "none";
}
public:
static void *handlertask(void *args)
{
threadpool *tp = static_cast<threadpool<t>*>(args);
string name = tp->getthreadname(pthread_self());
while (true)
{
tp->lock();
while (tp->isqueueempty())
{
tp->threadsleep();
}
t t = tp->pop();
tp->unlock();
t();
}
}
void start()
{
int len = threads_.size();
for (int i = 0; i < len; i++)
{
threads_[i].name = "thread -" + to_string(i);
pthread_create(&(threads_[i].tid), nullptr, handlertask, this); // this指针指向的是调用成员函数的对象(threadpool)
}
}
t pop()
{
t t = tasks_.front();
tasks_.pop();
return t;
}
void push(const t &in)
{
lock();
tasks_.push(in);
wakeup();
unlock();
}
static threadpool<t> *getinstance() // 懒汉模式
{
if (tp_ == nullptr)//减少申请锁和释放锁的次数
{
pthread_mutex_lock(&lock_); // 避免出现多个线程抢夺同一份资源
while (tp_ == nullptr)
{
tp_ = new threadpool<t>();
}
pthread_mutex_unlock(&lock_);
}
return tp_;
}
private:
threadpool(int num = defaultnum)
: threads_(num)
{
pthread_mutex_init(&mutex_, nullptr);
pthread_cond_init(&cond_, nullptr);
}
~threadpool()
{
pthread_mutex_destroy(&mutex_);
pthread_cond_destroy(&cond_);
}
private:
vector<threadinfo> threads_;
queue<t> tasks_;
pthread_mutex_t mutex_;
pthread_cond_t cond_;
static pthread_mutex_t lock_;
static threadpool<t> *tp_; // 懒汉模式
};
template <class t>
threadpool<t> *threadpool<t>::tp_ = nullptr;
template <class t>
pthread_mutex_t threadpool<t>::lock_ = pthread_mutex_initializer;以上就是linux基于tcp实现服务端客户端通信的详细内容,更多关于linux tcp服务端客户端通信的资料请关注代码网其它相关文章!
发表评论