当前位置: 首页 > 编程日记 > 正文

24、线程控制

线程有一套完整的与其有关的函数库可供调用,它们中的绝大多数函数名都以pthread_开头。为了调用这些函数库,必须在程序中包含头文件pthread.h,并且在比那一程序时使用选项-lpthread来链接线程库。

1、线程标识

就像每个进程有一个进程ID一样,每个线程也有一个线程ID。进程ID在整个系统中是唯一的,但线程ID只有在它所属的进程上下文中才有意义。

进程ID,用pid_t数据类型来表示,是一个非负整数。线程ID则用pthread_t数据类型来表示,实现的时候可以用一个结构来代表pthread_t数据类型,所以可移植的操作系统实现不能把它作为整数处理。因此必须使用函数来对两个线程ID进行比较。其函数原型如下:

#include <pthread.h>
int pthread_equal( pthread_t tid1, pthread_t tid2 );
返回值:若相等则返回非0值,否则返回0

用结构表示pthreadd_t数据类型的后果是不能用一种可移植的方式打印该数据类型的值。有时在程序调试过程中打印线程ID是非常有用的,而通常在其他情况下不需要打印线程ID。最坏的情况是:有可能出现不可移植的调试代码,当然这算不上是很大的局限性。

线程可以通过调用pthread_self函数获得自身的线程ID。其函数原型如下:

#include <pthread.h>pthread_t pthread_self(void);  

当线程需要识别以线程ID作为标识的数据结构时,函数pthread_self()可以于函数pthread_equal()一起使用。例如,主线程可能把工作任务放到一个队列中,用线程ID来控制每个工作线程处理哪些作业。

2、线程创建

启动程序后,产生的进程只有一条线程,称之为初始线程或主线程。在传统的UNIX进程模型中,每个进程只有一个控制线城。从概念上来讲,这与基于线程的模型中每个进程只包含一个线程是相同的的。在POSIX线程(pthread)的情况下,程序开始运行时,它也是以单进程中的单个控制线程启动的,在创建多个控制线程以前,程序的行为与传统的进程并没有什么区别。新增的线程可以通过调用pthread_create函数创建。其函数原型如下:

#include <pthread.h>
int pthread_create(pthread_t *restrict tidp,const pthread_attr_t *restrict attr,void *(*start_rtn)(void *), void *restrict arg);
返回值:若成功则返回0,否则返回错误编号

当pthread_create成功返回时,由tidp指向的内存单元被设置为新创建的线程的线程ID。attr参数用于定制各种不同的线程属性。线程属性在以后介绍,眼下暂时把它设置为NULL,创建默认属性的线程。

新创建的线程从start_rtn函数的地址开始运行,该函数只有一个无类型指针参数arg,如果需要向start_rtn函数传递的参数不止一个,那么需要把这些参数放到一个结构中,然后把这个结构的地址作为arg参数传入。

线程创建时并不能保证哪个线程会先运行:是新创建的线程还是调用线程。新创建的线程可以访问进程的地址空间,并且继承调用线程的浮点环境和信号屏蔽字,但是该线程的未决信号集被清除。

注意pthread函数在调用失败时通常会返回错误码,它们并不像其他的POSIX函数一样设置errno。每个线程都提供errno的副本,这只是为了与使用errno的现有函数兼容。在线程中,从函数中返回错误码更为清晰整洁,不需要依赖那些随着函数执行不断变化的全局状态,因而可以把错误的范围限制在引起出错的函数中。

示例:使用函数pthread_create()创建新线程

pthread_creat.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>

pthread_t ntid;void printids(const char *s) {pid_t pid;pthread_t tid;pid = getpid();tid = pthread_self();printf("%s pid %u tid %u (0x%x) \n", s, (unsigned int)pid,(unsigned int)tid, (unsigned int)tid); } void *thr_fn(void *arg) {printids("new thread : ");return((void *)0); }int main(void) {int err; err = pthread_create(&ntid, NULL, thr_fn, NULL);if(err != 0)printf("can't create thread: %s\n", strerror(err));printids("main thread: ");sleep(1);exit(0); }

编译运行结果如下:

这个实例有两个需要注意的地方:

(1)需要处理主线程和新线程之间的竞争。首先是主线程需要休眠,如果主线程不休眠,它就可能退出,这样在新线程有机会运行之前整个进程可能就已经终止了。这种行为特征依赖于操作系统中的线程实现和调度算法。

(2)新线程是通过调用pthread_self函数获取自己的线程ID,而不是从共享内存中读出或者从线程的启动例程中以参数的形式接收到。回忆pthread_create函数,它会通过第一个参数(tidp)返回新建线程的线程ID。在本例中,主线程把新线程ID存放在ntid中,但是新建的线程并不能安全地使用它,如果新线程在主线程调用pthread_create返回之前就运行了,那么新线程看到的是未经初始化的ntid的内容,这个内容并不是正确的线程ID。

3、线程终止

如果进程中的任一线程调用了exit、_Exit或者_exit,那么整个进程就会终止。与此类似,如果信号的默认动作是终止进程,那么,把该信号发送到线程会终止整个进程。

单个线程可以通过下列三种方式退出,在不终止整个进程的情况下停止它的控制流。

(1)线程只是从启动例程中返回,返回值是线程的退出码。

(2)线程可以被同一进程中的其他线程取消。

(3)线程调用pthread_exit。

1、函数pthread_exit()

函数原型如下

#include <pthread.h>
void pthread_exit(void *rval_ptr);

rval_ptr是一个无类型指针,与传给启动例程的单个参数类似。

示例:

程序给出了用自动变量(分配在栈上)作为pthread_exit的参数时出现的问题

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
struct foo {int a, b, c, d;};void printfoo(const char *s, const struct foo *fp)
{printf(s);printf("   structure at 0x%x\n", (unsigned)fp);printf("   foo.a = %d\n", fp->a);printf("   foo.b = %d\n", fp->b);printf("   foo.c = %d\n", fp->c);printf("   foo.d = %d\n", fp->d);
}void *
thr_fn1(void *arg)
{struct foo foo = {1, 2, 3, 4};printfoo("thread 1:\n", &foo);pthread_exit((void *)&foo);printfoo("thread 1:\n", &foo);
}void *thr_fn2(void *arg){printf("thread 2: ID is %d\n", pthread_self());pthread_exit((void *)0);}int main(void)
{int         err;pthread_t    tid1, tid2;struct foo    *fp;err = pthread_create(&tid1, NULL, thr_fn1, NULL);if(err != 0)printf("can't create thread 1: %s\n", strerror(err));err = pthread_join(tid1, (void *)&fp);if(err != 0)printf("can't join with thread 1: %s\n", strerror(err));
sleep(2);printfoo("parent: \n", fp);exit(0);
}

编译运行结果如下:

2、函数pthread_join()

进程中的其他线程可以通过调用pthread_join函数等待线程的结束,其函数原型如下:

#include <pthread.h>
int pthread_join(pthread_t thread, void **rval_ptr);
返回值:若成功则返回0,否则返回错误编号

调用线程将一直阻塞,直到指定的线程调用pthread_exit、从启动例程中返回或者被取消。如果线程只是从它的启动例程返回,rval_ptr将包含返回码。如果线程被取消,由rval_ptr指定的内存单元就置为PTHREAD_CANCELED。

可以通过调用pthread_join自动把线程置于分离状态,这样资源就可以恢复。如果线程已经处于分离状态,pthread_join调用就会失败,返回EINVAL。

如果对线程的返回值并不感兴趣,可以把rval_ptr置为NULL。在这种情况下,调用pthread_join函数将等待指定的线程终止,但并不获取线程的终止状态。

示例:获取已终止的线程的退出码。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>void *thr_fn1(void *arg){printf("thread 1 returning\n");return((void *)1);}void *thr_fn2(void *arg){printf("thread 2 exiting\n");pthread_exit((void *)2);}int main(void){int         err;pthread_t    tid1, tid2;void        *tret;err = pthread_create(&tid1, NULL, thr_fn1, NULL);if(err != 0)printf("can't create thread 1: %s\n", strerror(err));err = pthread_create(&tid2, NULL, thr_fn2, NULL);if(err != 0)printf("can't create thread 2: %s\n", strerror(err));err = pthread_join(tid1, &tret);if(err != 0)printf("can't join with thread 1: %s\n", strerror(err));printf("thread 1 exit code %d\n", (int)tret);err = pthread_join(tid2, &tret);if(err != 0)printf("can't join with thread 2: %s\n", strerror(err));printf("thread 2 exit code %d\n", (int)tret);exit(0);}

程序编译运行结果如下:

可以看出,当一个线程通过调用pthread_exit退出或者简单地从启动例程中返回时,进程中的其他线程可以通过调用pthread_join函数获得该线程的退出状态

pthread_create和pthread_exit函数的无类型指针参数能传递的数值可以不止一个,该指针可以传递包含更复杂信息的结构的地址,但是注意这个结构所使用的内存在调用者完成调用以后必须仍然是有效的,否则就会出现无效或非法内存访问。

3、函数pthread_cancel()

函数pthread_cancel()用于请求取消同一进程中的其他线程,其函数原型如下:

#include <pthread.h>
int pthread_cancel(pthread_t tid);
返回值:若成功则返回0,否则返回错误编号

在默认情况下,pthread_cancel函数会使得由tid标识的线程的行为表现为如同调用了参数为PTHREAD_CANCELED的pthread_exit函数,但是,线程可以选择忽略取消方式或是控制取消方式。注意,pthread_cancel并不等待线程终止,它仅仅提出请求。

线程可以安排它退出时需要调用的函数,这与进程可以用atexit函数安排进程退出时需要调用的函数是类似的。这样的函数称为线程清理处理程序(thread cleanup handler)。线程可以建立多个清理处理程序。处理程序记录在栈中,也就是说它们的执行顺序与它们注册时的顺序相反。

#include <pthread.h>
void pthread_cleanup_push(void (*rtn)(void *), void *arg);
void pthread_cleanup_pop(int execute);

当线程执行以下动作时调用清理函数(调用参数为arg,清理函数rtn的调用顺序是由pthread_cleanup_push函数来安排的):

  • 调用pthread_exit时。
  • 相应取消请求时。
  • 用非零execute参数调用pthread_cleanup_pop时。

如果execute参数置为0,清理函数将不被调用。无论哪种情况,pthread_cleanup_pop都将删除上次pthread_cleanup_push调用建立的清理处理程序。

这些函数有一个限制,由于它们可以实现为宏,所以必须在与线程相同的作用域中以匹配对的形式使用,pthread_cleanup_push的宏定义可以包含字符{,在这种情况下对应的匹配字符}就要在pthread_cleanup_pop定义中出现。

示例:如何使用线程清理处理程序。需要把pthread_cleanup_pop调用和pthread_cleanup_push调用匹配起来,否则,程序编译可能通不过。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
void 
cleanup(void *arg)
{printf("cleanup: %s\n", (char *)arg);
}void *
thr_fn1(void *arg)
{printf("thread 1 start\n");pthread_cleanup_push(cleanup, "thread 1 first hanlder");    pthread_cleanup_push(cleanup, "thread 1 second handler");printf("thread 1 push complete\n");if(arg)return((void *)1);pthread_cleanup_pop(0);pthread_cleanup_pop(0);return((void *)1);
}void *
thr_fn2(void *arg)
{printf("thread 2 start\n");pthread_cleanup_push(cleanup, "thread 2 first handler");pthread_cleanup_push(cleanup, "thread 2 second handler");printf("thread 2 push complete\n");if (arg)pthread_exit((void *)2);pthread_cleanup_pop(0);pthread_cleanup_pop(0);pthread_exit((void *)2);
}int
main(void)
{int        err;    pthread_t    tid1, tid2;void        *tret;err = pthread_create(&tid1, NULL, thr_fn1, (void *)1);if(err != 0)printf("can't create thread 1: %s\n", strerror(err));err = pthread_create(&tid2, NULL, thr_fn2, (void *)1);if(err != 0)printf("can't create thread 2: %s\n", strerror(err));err = pthread_join(tid1, &tret);if(err != 0)printf("can't join with thread 1: %s\n", strerror(err));printf("thread 1 exit code %d\n", (int)tret);err = pthread_join(tid2, &tret);if(err != 0)printf("can't join with thread 2: %s\n", strerror(err));printf("thread 2 exit code %d\n", (int)tret);exit(0);
}

编译运行结果如下:

从输出结果可以看出,两个线程都正确地启动和退出了,但是只调用了第二个线程的清理处理程序,所以如果线程是通过从它的启动例程中返回而终止的话,那么它的清理处理程序就不会被调用,还要注意清理处理程序是按照与它们安装时相反的顺序被调用的。

现在可以开始看出线程函数和进程函数之间的相似之处。表11-1总结了这些相似的函数。

进程原语和线程原语的比较

2012080513265968

4、线程的分离与结合

在默认情况下,线程的终止状态会保存一直等到对该线程调用pthread_join。如果线程已经处于分离状态线程的底层存储资源可以在线程终止时立即被收回。

在任何一个时间点上,线程是可结合的(joinable),或者是分离的(detached)。一个可结合的线程能够被其他线程收回其资源和杀死;在被其他线程回收之前,它的存储器资源(如栈)是不释放的。相反,一个分离的线程是不能被其他线程回收或杀死的,它的存储器资源在它终止时由系统自动释放。

线程的分离状态决定一个线程以什么样的方式来终止自己。线程的默认属性,即为非分离状态(即可结合的,joinable,需要回收),这种情况下,原有的线程等待创建的线程结束;只有当pthread_join()函数返回时,创建的线程才算终止,才能释放自己占用的系统资源。而分离线程不是这样子的,它没有被其他的线程所等待,自己运行结束了,线程也就终止了,马上释放系统资源。程序员应该根据自己的需要,选择适当的分离状态。

当线程被分离时,并不能用pthread_join函数等待它的终止状态。对分离状态的线程进行pthread_join的调用会产生失败,返回EINVAL。pthread_detach调用可以用于使线程进入分离状态。

#include <pthread.h>
int pthread_detach(pthread_t tid);
返回值:若成功则返回0,否则返回错误编号

参考资料:

线程

线程之线程的创建

线程之线程终止

Linux多线程,线程的分离与结合

线程同步

转载于:https://www.cnblogs.com/noticeable/p/8544420.html

相关文章:

Datawhale组队学习周报(第038周)

本周报总结了从 11月01日至11月07日&#xff0c;Datawhale组队学习的运行情况&#xff0c;我们一直秉承“与学习者一起成长的理念”&#xff0c;希望这个活动能够让更多的学习者受益。 第 30 期组队学习一共 8 门开源课程&#xff0c;共组建了 8 个学习群&#xff0c;参与的学…

OpenGL概念辨析: 窗口,视口,裁剪区域

1.窗口&#xff1a;这就不用解释了吧 2.视口&#xff1a;就是窗口中用来显示图形的一块矩形区域&#xff0c;它可以和窗口等大&#xff0c;也可以比窗口大或者小。只有绘制在视口区域中的图形才能被显示&#xff0c;如果图形有一部分超出了视口区域&#xff0c;那么那一部分是看…

java源码推荐_基于java的推荐系统实现源代码

【实例简介】常用推荐算法java实现~涉及多种相似度计算&#xff0c;比如cosine相似度&#xff0c;欧氏距离等~(recommand algirithm )【实例截图】【核心代码】RecommendSystemJavaCode└── Recommend└── src├── collaborative│ ├── cache│ │ ├── FileS…

ref与out的区别

前一段时间老用ref与out 感觉他们的效果差不多&#xff0c;就去网上查了一下他们的区别&#xff0c;网上说的概念性的东西太多了&#xff0c;后来通过自己的摸索发现他们有一个规律 ref: 在引用方法之外必须赋初值 static void TestRefAndRef(){string s1"test";Test…

【组队学习】【31期】组队学习内容详情

第31期 Datawhale 组队学习活动马上就要开始啦&#xff01; 本次组队学习的内容为&#xff1a; IOS开发基于Python的办公自动化吃瓜教程——西瓜书南瓜书LeetCode 刷题李宏毅机器学习&#xff08;含深度学习&#xff09;动手学数据分析SQL编程语言数据可视化&#xff08;Matpl…

区块链到底是什么?

2019独角兽企业重金招聘Python工程师标准>>> 欢迎大家前往腾讯云社区&#xff0c;获取更多腾讯海量技术实践干货哦~ 翻译人&#xff1a;ArrayZoneYour&#xff0c;该成员来自云社区翻译社 原文链接&#xff1a;https://www.investinblockchain.com/what-exactly-is-…

java怎么返回xml_java – 如何从Web服务返回XML

这可能是疯狂/愚蠢/愚蠢/冗长的问题之一,因为我是网络服务的新手.我想写一个Web服务,它将以XML格式返回答案(我正在使用我的服务进行YUI自动完成).我正在使用Eclipse和Axis2并遵循http://www.softwareagility.gr/index.php?qnode/21我希望以下列格式回复代码元素的数量可能因响…

jsp路径问题

绝对路径&#xff1a;/StudentInfo/images/login.jpg 相对路径&#xff1a;images/login.jpg 路径前面的第一个/代表tomcate目录下面的webapps这个文件夹 jsp的Advanced模版。。。默认有一个基准路径,所有写的路径都会变成绝对路径。 测试的时候发现&#xff0c;在IE下面可以正…

写一篇C语言入门第一讲

嗨~大家好~ 我是小白&#xff0c;最近才使用这个博客&#xff0c;我是一个计算机系的学生&#xff0c;我会在这里发一些我给我们班其他同学讲C语言入门的博文&#xff0c;希望大家能共享这些资料&#xff0c;当然了&#xff0c;我也很希望大家给我提出好的意见或建议。&#x…

李嘉骐:03 PyTorch模块与基础实战

深入浅出Pytorch 03 PyTorch模块与基础实战 内容属性&#xff1a;深度学习&#xff08;实践&#xff09;专题航路开辟者&#xff1a;李嘉骐、牛志康、刘洋、陈安东领航员&#xff1a;叶志雄航海士&#xff1a;李嘉骐、牛志康、刘洋、陈安东开源内容&#xff1a;https://githu…

math.hypot java_Java之Math类

Java之Math类#Java的Math类封装了很多与数学有关的属性和方法,后续遇到常用也会直接在这篇博客更新。。。###public static void t2() {System.out.println(Math.E);//比任何其他值都更接近 e(即自然对数的底数)的 double 值。System.out.println(Math.PI);//比任何其他值都更接…

ruby Mixin用法

module MyNA"China"attr:nameattr:agedef set_name(name)namenameenddef get_namereturn nameenddef set_age(age)ageageend endclass Testinclude My endtTest.new t.set_name("history") p t.get_name 转载于:https://www.cnblogs.com/wangwenfei/p/ruby…

delphi ScriptGate 调用JS

在 FireMonkey 使用 TWebBrowser 调用 Javascript函数并获取返回值以及 JavaScript 中调 Delphi 的函数/过程&#xff0c;普遍都在使用老掉牙的URL重定的方法&#xff0c;还要改 FMX 的源码&#xff0c;相当繁琐。 现在使用 ScriptGate 可轻易解决这个问题&#xff0c;ScriptGa…

【NCEPU】韩绘锦:扩散卷积神经网络

韩绘锦是华北电力大学数理系大四的学生&#xff0c;Datawhale成员/Dreamtech成员&#xff0c;也在天池比赛中取得了不错的成绩&#xff0c;现保送大连理工大学软件工程学院深造。 这篇图文是他在线下组队学习时&#xff0c;分享的内容。 希望参与我们组队学习的同学可以在微信…

java 解压与压缩代码_Java实现多文件压缩和解压缩代码详解

Java实现多文件压缩和解压缩代码import java.io.File;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.InputStream;import java.io.OutputStream;import java.util.Enumeration;import java.util.zip.ZipEntry;import java.util.zip.ZipFile;…

关系管理系统:js代码生成select的出生日期

//page初始调用function pageInit() {makeYear();makeMonth();makeDay();} //产生Year function makeYear(){var year document.getElementById("year");for(var i1901;i<new Date().getYear();i){var option document.createElement("option");optio…

【组队学习】【31期】IOS开发

IOS开发 航路开辟者&#xff1a;李岳昆、易远哲领航员&#xff1a;杨皓博航海士&#xff1a;李岳昆、易远哲 基本信息 开源内容&#xff1a;https://github.com/datawhalechina/team-learning-program/tree/master/IOS内容属性&#xff1a;公测课程内容说明&#xff1a;iOS开…

amh支持java吗_跟我学Android之三 常用视图

目标掌握视图的概念。明白Activity与Widget的区别。掌握XML方式布局界面的特点和一些基本特性。掌握几种常见基本视图的用法学会使用代码方式进行界面布局的方法。熟练掌握界面程序的事件驱动模型视图(View)是可视化的界面元素,任何可视化组件都需要从android.view.View类继承,…

Linux 终端命令行提示符的艺术--PS1进阶

话不多说&#xff0c;先瞅瞅我的命令行提示符&#xff08;有点大&#xff09;&#xff1a; 图中命令行解释&#xff1a;┌[阳历日期/农历日期 时间]├[当前目录下目录数当前目录下文件数][当前绝对目录]└[用户名主机名-第几个终端 ╰_╯] 相关配置文件 全局配置文件&#xff1…

Centos 7 冗余备份磁盘配置介绍

Centos 7 冗余备份磁盘配置介绍我们上一盘介绍了Centos 7 磁盘阵列配置介绍&#xff0c;今天继续上一篇的配置介绍&#xff0c;通过上一篇的配置介绍我们发现了一个问题。&#xff0c;运维人员需要在硬盘硬件出现故障后&#xff0c;手动增加新的硬盘进去&#xff0c;这样很不方…

【组队学习】【31期】基于Python的办公自动化

基于Python的办公自动化 航路开辟者&#xff1a;牧小熊、刘雯静、张晓东、吴争光、隆军领航员&#xff1a;六一航海士&#xff1a;牧小熊、李显、刘羽中、王晓亮 基本信息 开源内容&#xff1a;https://github.com/datawhalechina/team-learning-program/tree/master/Office…

java urlconn 下载慢_使用HttpURLConnection下载文件时出现 java.io.FileNotFoundException彻底解决办法...

import java.io.File;import java.io.IOException;import java.io.InputStream;import java.io.RandomAccessFile;import java.net.HttpURLConnection;import java.net.URL;import java.net.URLEncoder;/*** 多线程下载* author bing**/public class OmbDownloadOfThreadsUtil …

java中Array和ArrayList区别

2019独角兽企业重金招聘Python工程师标准>>> 1&#xff09;精辟阐述&#xff1a; 可以将 ArrayList想象成一种“会自动扩增容量的Array”。 2&#xff09;Array&#xff08;[]&#xff09;&#xff1a;最高效&#xff1b;但是其容量固定且无法动态改变&#xff1b; …

【组队学习】【31期】 吃瓜教程——西瓜书+南瓜书

吃瓜教程——西瓜书南瓜书 航路开辟者&#xff1a;谢文睿、秦州领航员&#xff1a;张海腾航海士&#xff1a;谢文睿、秦州 基本信息 开源内容&#xff1a;https://github.com/datawhalechina/pumpkin-bookB 站视频&#xff1a;https://www.bilibili.com/video/BV1Mh411e7VU内…

设计模式之“代理模式”

代理&#xff08;Proxy&#xff09;模式给某一个对象提供一个代理&#xff0c;并由代理对象控制对原对象的引用。 代理模式的英文叫做Proxy或Surrogate&#xff0c;中文都可译成"代理"。所谓代理&#xff0c;就是一个人或者一个机构代表另一个人或者另一个机构采取行…

php更新数据库时间戳,关于Thinkphp5 里面数据库自动更新与创建时间的问题

我们有时候往数据库里面写入新的一条数据 时&#xff0c;可能需要自动更新时间、自动创建时间、这样就可以方便我们、从而大大减小我们的代码量&#xff1b;不过在TP5里面有一个小规律&#xff0c;就是save()与insert()语句的区别&#xff1b;1、我们先看一下TP5里面自动更新时…

【组队学习】【31期】LeetCode 刷题

LeetCode 刷题 航路开辟者&#xff1a;杨世超领航员&#xff1a;刘军航海士&#xff1a;杨世超、李彦鹏、叶志雄、赵子一 基本信息 开源内容&#xff1a;https://github.com/itcharge/LeetCode-Py开源电子书&#xff1a;https://algo.itcharge.cn内容属性&#xff1a;合作课…

DOM---文档对象模型(Document Object Model)的基本使用

一、DOM简介  文档对象模型&#xff08;Document Object Model&#xff0c;简称DOM&#xff09;&#xff0c;是W3C组织推荐的处理可扩展置标语言的标准编程接口。它是一种与平台和语言无关的应用程序接口(API),它可以动态地访问程序和脚本,更新其内容、结构和www文档的风格(目…

剑指offer--3题

题目&#xff1a;输入一个整形数组&#xff0c;数组里有正数也有负数。数组中连续的一个或多个整数组成一个子数组&#xff0c;每个子数组都有一个和。求所有子数组的和的最大值。要求时间复杂度为O(n)。 例如输入的数组为1, -2, 3, 10, -4, 7, 2, -5&#xff0c;和最大的子数组…

php可以打印一个页面,利用html实现分页打印功能的实例详解

本篇介绍利用html实现分页打印功能的实例详解&#xff0c;有些不想打印出来的分页打印的都可以应用这类样式进行控制 在非打印时是无效的。页面打印/* 应用这个样式的在打印时隐藏 */.noPrint {display: none;}/* 应用这个样式的&#xff0c;从那个标签结束开始另算一页&#x…