Linux C++ 200行完成线程池类
文章目录
- 1、atomic使用
- 2、volatile关键字
- 3、条件变量
- 4、成员函数指针使用
- 5、线程池
- 6、主线程先退出对子线程影响
- 7、return、exit、pthread_exit区别
- 8、进程和线程的区别
1、atomic使用
原子操作,不可分割的操作,要么完整,要么不完整。
#include <pthread.h>
#include <unistd.h>
#include <iostream>
#include <atomic>
using namespace std;atomic<int> g_acount;
int g_count = 0;
void* ThreadFunc(void* threadData)
{for(int i=0;i<1000000;i++){g_count++;g_acount++;}
}int main(int argc, const char** argv)
{pthread_t pid1,pid2;int err = pthread_create(&pid1,NULL,ThreadFunc,NULL);if(err!=0){cout<<"thread fail---"<<endl;exit(0);}err = pthread_create(&pid2,NULL,ThreadFunc,NULL);if(err!=0){cout<<"thread fail---"<<endl;exit(0);}pthread_join(pid1,NULL);pthread_join(pid2,NULL);cout<<"g_count:"<<g_count<<endl;cout<<"g_acount:"<<g_acount<<endl;return 0;
}
makefile
all: pthreadTextpthreadText:pthreadText.cppg++ -o pthreadText pthreadText.cpp -pthread -std=c++11
运行结果:
2、volatile关键字
用volatile关键字声明的变量,会告诉编译器,这个变量随时可能发生变化,编译器在编译的时候就不会对变量进行激进的优化,每次去读取的时候都会去内存中取,相反,如果编译器进行量优化,可能读取的时候去寄存器去读取这个值,三种特性:易变的、不可优化的、顺序执行的。
3、条件变量
条件本身(while((g_msgQueue.size() == 0) && isRuning == false))是由互斥量保护的,线程在发生改变之前首先锁住互斥量,其他线程不会察觉到这种改变,因为互斥量必须锁住才能计算条件。
#include <iostream>
#include <signal.h>
#include <unistd.h>
#include <pthread.h>
#include <list>
using namespace std;// 初始化
pthread_cond_t g_cond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t g_mutex = PTHREAD_MUTEX_INITIALIZER;struct msgStr
{char name[256];int age;int id;
};list<msgStr*> g_msgQueue;
bool isRuning = false;void* outCache(void* data)
{while(true){pthread_mutex_lock(&g_mutex);while((g_msgQueue.size() == 0) && isRuning == false){pthread_cond_wait(&g_cond,&g_mutex);}if(isRuning){pthread_mutex_unlock(&g_mutex);break;}// 消息处理msgStr* jobbuf= g_msgQueue.front();g_msgQueue.pop_front();pthread_mutex_unlock(&g_mutex);// 消息处理cout<<"tid:"<<pthread_self()<<"name:"<<jobbuf->name<<"age:"<<jobbuf->age<<"id:"<<jobbuf->id<<endl;usleep(1000);delete jobbuf;jobbuf = NULL;}
}void inCache(int sig)
{// 收到15这个信号,向消息队列中添加数据if(sig == 15){struct msgStr* msg = NULL;pthread_mutex_lock(&g_mutex);for(int i=0;i<1000;i++){msg = new msgStr();sprintf(msg->name,"name--%d",i);msg->age = i;msg->id = 1000+i;g_msgQueue.push_back(msg);}pthread_mutex_unlock(&g_mutex);pthread_cond_broadcast(&g_cond);}if(sig == 10){isRuning = true;}
}int main()
{// 作为向消息队列中添加数据的函数signal(15,inCache);pthread_t pid1,pid2;pthread_create(&pid1,NULL,outCache,NULL);pthread_create(&pid2,NULL,outCache,NULL);pthread_join(pid1,NULL);pthread_join(pid2,NULL);return 0;
}
4、成员函数指针使用
#include <iostream>
using namespace std;class Test
{
public:Test();~Test(){}void func(int a,int b){cout<<"Test:"<<a<<endl;cout<<"Test:"<<b<<endl;}void func1(int a,int b){cout<<"Test:"<<a<<endl;cout<<"Test:"<<b<<endl;}
};// 函数指针
typedef void (Test::*handler)(int a,int b);const handler handArray[] =
{NULL,NULL,NULL,NULL,NULL,&Test::func1,&Test::func,
};Test::Test()
{(this->*handArray[5])(1,2);
}int main()
{Test t;(t.*handArray[6])(3,5);return 0;
}
makefile
g++ -o main pthreadPoolText.cpp
5、线程池
线程池概率:提前创建多个线程,并通过一个类来统一管理这一堆线程。
工作流程:来了一个任务,从线程池中找一个空闲的线程去处理这个任务,做完任务,循环回来等待新任务,等待新任务,
由pthreadPool.h、pthreadPool.cpp两个文件组成。
1、createPthread函数:创建线程全部线程,并将每个线程结构,放入容器中,函数中的goto语句部分,是为了保证所有线程运行起来,并且都处于pthread_cond_wait未激发状态等待。
2、call函数:中pthread_cond_broadcast唤醒一个或者多个线程,并且记录当前工作线程是否够用,每过十秒钟打印一下信息。
3、stopAll函数:唤醒一个或多个线程,并且释放资源
4、inMsgRecvQueueAndSignal函数:将消息插入消息队列中,并调用call函数。
pthreadPool.h
#ifndef __PTHREADPOOL_H_
#define __PTHREADPOOL_H_
#include <vector>
#include <atomic>
#include <pthread.h>
#include <iostream>
#include <list>
#include <unistd.h>
using namespace std;struct student
{char name[256];unsigned int age;int id;
};class pthreadPool
{
public:pthreadPool();~pthreadPool();public:bool createPthread(int threadNUm = 5);void stopAll();void call();void inMsgRecvQueueAndSignal(char* buf);private:static void* threadFunc(void* threadData);void clearMsgRecvQueue();void msgDispose(char* jobbuf);
private:struct pthreadItem{bool isruning;pthreadPool* _pThis;pthread_t _Handle;pthreadItem(pthreadPool* pthis):_pThis(pthis),isruning(false){}~pthreadItem(){}};
private:static pthread_cond_t m_pthreadCond; // 条件变量static pthread_mutex_t m_pthreadMutex; // 互斥量static bool m_shutdown; // 线程退出标识int m_iThreadNum; // 要创建的线程数time_t m_iLastTime; // 上次线程不够用,时间记录atomic<int> m_iRunThreadNum; // 正在运行线程数量 原子操作vector<pthreadItem*> m_vThread; // 线程容器list<char*> m_msgRecvQueue; // 消息队列int m_iRecvQueueCount; // 收消息队列大小
};#endif // !__PTHREADPOOL_
pthreadPool.cpp文件
#include "pthreadPool.h"pthread_cond_t pthreadPool::m_pthreadCond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t pthreadPool::m_pthreadMutex = PTHREAD_MUTEX_INITIALIZER;
bool pthreadPool::m_shutdown = false;pthreadPool::pthreadPool()
{// 运行的线程数为0,时间为0,消息数0m_iRunThreadNum = 0;m_iLastTime = 0;m_iRecvQueueCount = 0;
}pthreadPool::~pthreadPool()
{clearMsgRecvQueue();
}bool pthreadPool::createPthread(int threadNum)
{if(!threadNum) return false;m_iThreadNum = threadNum;pthreadItem* item;int errCode = 0;for(int i=0;i<threadNum;i++){item = new pthreadItem(this);errCode = pthread_create(&(item->_Handle),NULL,pthreadPool::threadFunc,item);if(errCode!=0){cout<<"线程创建失败:"<<i<<endl;return false;}m_vThread.push_back(item);}vector<pthreadItem*>::iterator iter;
lblfor:// goto语句作用,为了所有线程都正常启动,并且卡在pthread_cond_wait()这for(iter = m_vThread.begin();iter!=m_vThread.end();iter++){if((*iter)->isruning == false){usleep(100*1000); // 单位是微秒,100毫秒goto lblfor;}}return true;
}void* pthreadPool::threadFunc(void* threadData)
{pthreadItem* pThread = (pthreadItem*)threadData;pthreadPool* pPoll = pThread->_pThis;int errCode = 0;pthread_t tid = pthread_self();while(true){// 拿锁errCode = pthread_mutex_lock(&m_pthreadMutex);if(errCode!=0){cout<<"pthread_mutex_lock fail threadFunc errCode"<<errCode<<endl;return (void*)0;}while((pPoll->m_msgRecvQueue.size() == 0) && m_shutdown == false){if(pThread->isruning == false)pThread->isruning = true;// 整个程序初始化的时候,保证所有线程都卡在这里// 当线程走到这里,就会释放锁,处于未激发状态// 一旦被激发,就会去拿锁pthread_cond_wait(&m_pthreadCond,&m_pthreadMutex);}// 判断线程退出条件if(m_shutdown){pthread_mutex_unlock(&m_pthreadMutex);break;}// 走到这里可以去消息处理// 返回第一个元素,没有检查是否存在,走下来就说明有消息char* jobbuf = pPoll->m_msgRecvQueue.front();pPoll->m_msgRecvQueue.pop_front(); // 消息队列数减1--pPoll->m_iRecvQueueCount;// 可以解锁了pthread_mutex_unlock(&m_pthreadMutex);// 能走到这里表示有消息,并且线程正在处理这个消息// 正在工作的线程数加1,原子操作++pPoll->m_iRunThreadNum;// 消息处理//cout<<"消息处理开始:"<<tid<<endl;//sleep(3);//cout<<"消息处理结束:"<<tid<<endl;// 消息处理函数pPoll->msgDispose(jobbuf);// 消息处理结束//释放消息内存,运行线程数--++pPoll->m_iRunThreadNum;}return (void*)0;
}void pthreadPool::msgDispose(char* jobbuf)
{pthread_t tid = pthread_self();struct student* stu = (struct student*)jobbuf;cout<<"tid:"<<tid<<" name:"<<stu->name<<" age:"<<stu->age<<" id:"<<stu->id<<endl;if(stu!=NULL){delete stu;stu = NULL;}
}void pthreadPool::call()
{// 唤醒一个等待该条件的线程,也可能是多个,也就是可以唤醒卡在pthread_cond_waitint errCode = pthread_cond_signal(&m_pthreadCond);if(errCode!=0){cout<<"call fail"<<endl;return;}// 如果工作线程数==开辟线程数需要扩容if(m_iRunThreadNum == m_iThreadNum){time_t currentime = time(NULL);if(currentime-m_iLastTime >10){m_iLastTime = currentime;cout<<"Call()发现线程池中当前空闲线程数量为0,需要考虑扩容"<<endl;}}return;
}void pthreadPool::stopAll()
{if(m_shutdown)return;m_shutdown = true;int errCode = pthread_cond_broadcast(&m_pthreadCond);if(errCode!=0){cout<<"stopAll faile"<<endl;return;}// 等待所有线程结束vector<pthreadItem*>::iterator iter;for(iter=m_vThread.begin();iter!=m_vThread.end();iter++){pthread_join((*iter)->_Handle,NULL);if((*iter))delete *iter;}m_vThread.clear();pthread_cond_destroy(&m_pthreadCond);pthread_mutex_destroy(&m_pthreadMutex);cout<<"成功返回,线程池中线程全部正常退出"<<endl;return;
}void pthreadPool::clearMsgRecvQueue()
{while(!m_msgRecvQueue.empty()){char* buf = m_msgRecvQueue.front();m_msgRecvQueue.pop_front();if(buf!=NULL){delete buf;buf = NULL;}}
}void pthreadPool::inMsgRecvQueueAndSignal(char* buf)
{// 先互斥住int errCode = pthread_mutex_lock(&m_pthreadMutex);if(errCode!=0){cout<<"inMsgRecvQueueAndSignal faile lock"<<endl;}m_msgRecvQueue.push_back(buf);++m_iRecvQueueCount;errCode = pthread_mutex_unlock(&m_pthreadMutex);if(errCode!=0){cout<<"inMsgRecvQueueAndSignal faile unlock"<<endl;}// 激发线程做事call();return;
}
main函数文件测试代码
#include "pthreadPool.h"int main()
{pthreadPool* pool = new pthreadPool();pool->createPthread(6);for(int i=0;i<1000;i++){struct student* stu = new student();sprintf(stu->name,"name-%d",i);stu->age = i;stu->id = 1000+i;pool->inMsgRecvQueueAndSignal((char*)stu);}pool->stopAll();if(pool!=NULL){delete pool;pool = NULL;}pthread_exit(0);
}
makefile
all:pthreadPoolpthreadPool:pthreadPool.h pthreadPool.cpp pthreadPoolText.cppg++ -o pthreadPool pthreadPool.cpp pthreadPoolText.cpp -pthread -std=c++11
6、主线程先退出对子线程影响
观察一下代码:发现主线程退出之后,子线程没有继续打印,也退出了。
造成原因:主线程执行return 之后调用量glibc库里面的exit函数进行清理处理之后,调用系统调用_exit函数进行进程退出,所以并非是主线程退出,导致子线程退出的。
void* func(void* data)
{while(true){cout<<"child loops"<<endl;}return NULL;
}int main()
{ pthread_t pid;int errCode = pthread_create(&pid,NULL,func,NULL);sleep(1);cout<<"main exit"<<endl;return 0;
}
7、return、exit、pthread_exit区别
return返回到调用者
exit 退出当前进程
pthread_exit退出当前线程
8、进程和线程的区别
进程优缺点
进程优点:具有独立的地址空间,隔离性、稳定性比较好,是由操作系统管理,只要系统不出问题,一个进程的错误不会影响到其他进程。
进程缺点:共享资源麻烦
线程优缺点
线程优点:共享进程的资源,创建销毁,切换简单,速度快,占用内存小,CPU利用率高
线程缺点:需要程序员管理,相互影响几率大,一个线程挂掉将导致整个进程挂掉。
总结
一般需要频繁销毁喝创建,要处理大量运算数据,又要很好显示界面及时性比较高的,因为像这些消耗大量CPU,推荐是一个多线程,一般服务器对稳定性比较高的程序推荐使用多进程。
进程之间是如何通信的
可以通过管道pipe,信号量,共享内存,socket套接字,消息队列, 根据信息量大小,进行选择。
线程之间如何通信的
全局变量。或者自定义的消息通信机制,因为线程间共享进程的资源,所以没有像进程中用于数据交换的方式,它通信的主要目的是为了线程同步
多线程同步和互斥有几种方法
线程同步:互斥锁、信号量、信号量
互斥锁:拥有两种状态,lock和unlock,当互斥锁由某个线程持有时,互斥锁状态就变为lock,之后只有该线程有权利打开锁,其他想要获取互斥锁必须都阻塞,直到解锁。
信号量:信号量是一个计数器,用于控制访问有限共享资源数
条件变量:可以让线程满足特定条件才运行,必须搭配互斥锁一起使用。
相关文章:
Linux C++ 200行完成线程池类
文章目录1、atomic使用2、volatile关键字3、条件变量4、成员函数指针使用5、线程池6、主线程先退出对子线程影响7、return、exit、pthread_exit区别8、进程和线程的区别1、atomic使用 原子操作,不可分割的操作,要么完整,要么不完整。 #includ…...
C语言指针剖析(初阶) 最详细!
什么是指针?指针和指针类型野指针指针运算指针和数组二级指针指针数组什么是指针?指针是内存中一个最小单元的编号,也就是地址。1.把内存划分为一个个的内存单元,一个内存单元的大小是一个字节。2.每个字节都给定唯一的编号&#…...
AcWing语法基础课笔记 第三章 C++中的循环结构
第三章 C中的循环结构 学习编程语言语法是次要的,思维是主要的。如何把头脑中的想法变成简洁的代码,至关重要。 ——闫学灿 学习循环语句只需要抓住一点——代码执行顺序! while循环 可以简单理解为循环版的if语句。If语句是判断一次…...
A simple freeD tracking protocol implementation written in golang
可以使用的go版本freed调试代码 可以通过udp发送和接收数据 What is freeD? freeD is a very simple protocol used to exchange camera tracking data. It was originally developed by Vinten and is now supported by a wide range of hard- and software including Unreal…...
简约精美电商小程序【源码好优多】
简介 一款开源的电商系统,包含微信小程序和H5端,为大中小企业提供移动电子商务优秀的解决方案。 后台采用Thinkphp5.1框架开发,执行效率、扩展性、稳定性值得信赖。并且Jshop小程序商城上手难度低,可大量节省定制化开发周期。 功…...
全网详解 .npmrc 配置文件:比如.npmrc的优先级、命令行,如何配置.npmrc以及npm常用命令等
文章目录1. 文章引言2. 简述.npmrc3. 配置.npmrc3.1 .npmrc配置文件的优先级3.2 .npmrc设置的命令行3.3 如何设置.npmrc4. 配置发布组件5. npm常用命令6. 重要备注6.1 yarn6.2 scope命名空间6.3 镜像出错1. 文章引言 今天在某低代码平台开发项目时,看到如下编译配置…...
从0开始学python -31
Python3 模块-1 在前面的几个章节中我们基本上是用 python 解释器来编程,如果你从 Python 解释器退出再进入,那么你定义的所有的方法和变量就都消失了。 为此 Python 提供了一个办法,把这些定义存放在文件中,为一些脚本或者交互…...
Jenkins的使用教程
介绍: Jenkins是一个开源软件项目,是基于Java开发的一种持续集成工具,用于监控持续重复的工作,旨在提供一个开放易用的软件平台,使软件的持续集成变成可能。 目的: 最重要目的就是把原来分散在各个机器上繁杂的工作全部…...
1.Maven的坐标和依赖
【maven坐标】1.groupId: 通常与域名反向一一对应2.artifactId: 通常使用实际项目名称3.version: 项目当前版本号4.packaging:maven项目的打包方式,默认是jar5.classifier: 定义构建输出的一些附属构件,例如:nexus-indexer-2.0.0.…...
Jenkins 笔记
Jenkins brew install jenkins-lts brew services restart jenkins-lts brew services stop jenkins-lts b999ff5683464346b6d083f894968121 l 软件构建自动化 :配置完成后,CI系统会依照预先制定的时间表,或者针对某一特定事件,…...
Python和Java语言,哪个更适合做自动化测试?
经常有测试新手问我:Python和Java语言,哪个更适合做自动化测试?本来想简单的回答一下的,但又觉得对不起大家对小编的信任。因此,小编今天专门写了一篇文章来回答这个问题。欢迎各位大佬补充~1、什么是自动化测试&#…...
互联网的路由选择协议
互联网的路由选择协议 文章目录互联网的路由选择协议路由选择协议的几个概念分层次路由选择协议内部网关协议RIP协议距离向量算法RIP协议的报文格式内部网关协议OSPFOSPF的报文格式✨OSPF的特点外部网关协议BGPBGP的报文格式参考本篇主要讨论的是路由表中的路由是如何得出来的。…...
接口幂等性处理
1.Token 机制: a首先客户端请求服务端,获取一个 token,每一次请求都获取到一个全新的 token(当然这个 token 会有一个超时时间),将 token 存入 redis 中,然后将 token 返回给客户端。 b客户端…...
数字孪生智慧机场:透视数字化时代下的航空运营
在《智慧民航建设路线图》文件中,民航局明确指出,智慧机场是实现智慧民航的四个核心抓手之一。这一战略性举措旨在推进数字化技术与航空产业的深度融合,为旅客提供更加智能化、便捷化、安全化的出行服务,进一步提升我国民航发展的…...
SpringBoot 文件上传后查看404的问题和解决404后需要访问两次才能查看的问题
文件上传、图片上传的实现见这个: SpringBootVue 实现头像上传功能_Teln_小凯的博客-CSDN博客 在实现上面的功能后,发现查看图片的时候提示404,解决这个方法如下: 1、配置资源静态文件映射 第一个参数是页面请求的地址&#x…...
定时任务使用总结
定时任务表达式生成工具网站:https://cron.qqe2.com/定时任务选型:xxl-job 官方文档:https://www.xuxueli.com/xxl-job/安装定时任务调度中心 xxl-job-admin第一步、先导入xxl-job的数据库:地址:https://gitee.com/xux…...
Jira和Confluence Server版终止支持倒计时365天,企业应对策略汇总
本文对Atlassian最新的Server版政策进行了解读,并给出应对方案;同时我们也将国内热门的替代工具与jira进行了比较细致的对比,以及介绍替换的优惠政策等。今天是2023年2月15日,距离 Atlassian 旗下 Jira、Confluence 等系列产品中国…...
GEE学习笔记九十一:栅格影像叠置分析
最近发现好多人都在问一个问题,两张影像如何取其相交区域?其实这个问题简单来讲就是多张栅格影像进行叠加分析。在GEE中栅格影像不像矢量数据那样有直接的函数来做数据分析,需要我们自己手动写一些代码来实现这些操作。要实现这个功能有很多方…...
linux系统编程入门
一、搭建环境 1、安装 Linux 系统(虚拟机安装、云服务器) https://releases.ubuntu.com/bionic/ 2、安装 XSHELL、XFTP https://www.netsarang.com/zh/free-for-home-school/ 3、安装 visual studio code https://code.visualstudio.com/ 4、Linu…...
JS代码安全防护常见的方式
文章目录1. 常量的混淆1.1 十六进制字符串1.2 unicode字符串1.3 字符串的ASCII码混淆1.4 字符串常量加密1.5 数值常量加密2. 增加逆向分析难度2.1 数组混淆2.2 数组乱序2.3 花指令2.4 jsfuck3. 代码执行流程的防护3.1 流程平坦化3.2 逗号表达式4. 其他代码防护方案4.1 eval加密…...
PHP(13)HTTP协议
PHP(13)HTTP协议一、HTTP请求1. 请求行2. 请求头3. 请求体二、HTTP响应1. 响应行2. 响应头三、设置HTTP响应四、模拟HTTP请求一、HTTP请求 1. 请求行 请求行独占一行。形式:请求方式 资源路径 协议版本号 GET /index.php HTTP/1.1 2. 请求…...
基于支持向量机 (SVM) 用php实现预测气温
Windows 10自带的天气应用有一个基于历史数据预测气温的功能,有一定的参考价值。那么如何去实现这一功能呢?本文采用php进行实现。 使用机器学习方法实现预测当日气温的算法需要涵盖许多的步骤,以下是一种基于支持向量机 (SVM) 的算法的简化…...
MySQL(五)
通过索引进行优化 索引基本知识 索引的优点 1、大大减少了服务器需要扫描的数据量2、帮助服务器避免排序和临时表3、将随机io变成顺序io 索引的用处 1、快速查找匹配WHERE子句的行2、从consideration中消除行,如果可以在多个索引之间进行选择,mysql通常会使用找到…...
Linux常用命令2
目录1.查找find(1)普通用法(2)组合用法2.xargs命令3.管道符4.查看文件内容(1)查看两个文件的差别:diff file1 fille2(2)正序查看文件内容cat(3)倒序查看文件内容tac(4)分页查看文件内容more(5)分页查看文件内容less(6)…...
『C/C++养成计划』Visual Studio Code编辑器配置(外观通用型扩展Minmal)
Visual Studio Code编辑器配置(外观&通用型扩展&Minmal)! 文章目录 一. vscode配置外观|通用型扩展1.1. 色彩主题配置扩展(GitHub Theme)1.2. 图标主题扩展(Material Icon Theme)1.3. 代码高亮扩展(better-comments)1.4. 错误警告扩展(error lens)1.5. 执行代码扩展(c…...
设计模式(适配器模式)
设计模式(适配器模式) 第二章 设计模式之适配器模式(Adapter) 一、Adapter模式介绍 适配器模式位于实际情况和需求之间,填补两者之间的差距。 二、示例程序1(使用继承的适配器) 1.示例程序示…...
在基于全志D1s的芒果派麻雀上运行国产开源rt-smart系统
想必RT-Thread系统大家不陌生了,RT-Thread Smart(简称 rt-smart)是基于 RT-Thread 操作系统衍生的新分支,面向带 MMU,中高端应用的芯片,例如 ARM Cortex-A 系列芯片,MIPS 芯片,带 MM…...
【代码随想录训练营】【Day15】第六章|二叉树|层序遍历|226.翻转二叉树|101.对称二叉树
层序遍历 题目详细:LeetCode.102 层序遍历与上一节讲的三种遍历方式有所不同,层序遍历是指按从上到下,从左到右的顺序,逐层地遍历二叉树的节点。 从其节点的遍历顺序上观察,我们可以发现其跟广度优先遍历࿰…...
基于圆展开自适应三边测量算法的室内定位
基于圆展开自适应三边测量算法的室内定位 具有无线通信功能的移动设备的日益普及刺激了室内定位服务的增长。室内定位用于实时定位设备位置,方便访问。然而,由于大量障碍物,与室外定位相比,室内定位具有挑战性。全球定位系统非常适…...
使用中断子系统实现对LED灯的控制
中断顶半部:不允许耗时操作 代码流程: 1、基于字符设备驱动的注册(手动/自动) 2、基于设备树文件的自定义完成(myled, myirq) 2、基于GPIO子系统实现led的点亮(流水/测试文件控制) 3、中断子系统操作流程 …...
wordpress 获取用户密码/推广衣服的软文
直接赋值 可能创建一个或者不创建对象,如果”aaa”这个字符串在java String池里不存在,会在java String池里创建一个创建一个String对象(“aaa”)。 然后str1指向这个内存地址,无论以后用这种方式创建多少个值为”aaa”的字符串对象&#x…...
关于重新建设网站的申请/百度招聘官网首页
在FineReport中,会出现希望直接将txt、excel等文件整个保存在数据库中进行备份,并且希望通过FineReport制作报表将这些文件下载下来的情况。 上传:使用文件控件上传文件,以二进制流保存至数据库字段中(字段类型必须是…...
python做网站好吗/搜索引擎优化有哪些要点
电脑能力主要看处理器和显卡。办公用途,平面设计类吃处理器的能力。游戏,3d设计类吃显卡能力。不管那种需求,内存容量都是需要有合理的选择(类如看机器是否支持扩展)。不管台式本子都一样。本子的能力和选择问题下面说法参考。~&a…...
网站审核备案 几天/关键词优化的原则
我有一个密码保护Excel文件的问题。情况是,我有一个zip文件,其中有一个Excel文件。我需要编写一个Java程序,以密码保护Excel文件。因此,用户应该能够解压缩文件(压缩文件无需密码保护)。但是,Excel需要使用密码保护。当…...
萍乡网站推广/手机seo关键词优化
{*rule !important}这个css规则当今在网页制作的时候的普及已经非常流行了,以前我对它的理解就停留在‘浏览器是否识别阶段’ 而没有真正去研究过,可是现在发生了变化。众所周知,!important这个规则对Ie6.0,Ie7.0和Firefox能写hack࿰…...
通化公司做网站/搜索引擎排名竞价
第1篇:六年级上册数学圆的知识点整理一、认识圆1、圆的定义:圆是由曲线围成的一种平面图形。2、圆心:将一张圆形纸片对折两次,折痕相交于圆中心的一点,这一点叫做圆心。一般用字母o表示。它到圆上任意一点的距离都相等…...