Linux编程基础 6.2:线程同步

2 线程同步

  • 线程同步中的“同步”与生活中大家认知的“同步”略有不同,“同”不指同时,其主旨在于协同步调,按预定的先后次序执行线程;
  • 之所以需要实现线程同步,是因为若不对线程的执行次序加以控制,可能会出现数据混乱。

出现与事件有关的错误的原因有三个:

  • (1)资源共享;
  • (2)调度随机;
  • (3)线程间缺乏必要的同步机制。

Linux系统实现线程同步的方式常用的有三种:

  • 互斥锁;
  • 条件变量;
  • 信号量。

2.1 互斥锁

使用互斥锁的实现线程同步时主要操作分为四步:
①初始化互斥锁:pthread_mutex_init
②加锁:pthread_mutex_lock
③解锁:pthread_mutex_unlock
④销毁锁:pthread_mutxe_destroy

2.1.1 初始化互斥锁

#include <pthread.h>int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);

功能:初始化互斥锁。

参数说明

  • mutex:一个传入传出参数:
    – ①pthread_mutext_t的本质是结构体,为简化理解,读者可将其视为整型;
    – ②变量mutex只有两种取值:0和1,加锁:mutex-1;解锁:mutex+1;
    – ③参数mutex之前的restrict是一个关键字,该关键字用于限制指针,功能是告诉编译器,所有修改该指针指向内容的操作,只能通过本指针完成。
  • attr:一个传入传出参数,代表互斥量的属性,通常传NULL,表示使用默认属性。

返回值说明

  • 成功:返回0;
  • 不成功:返回errno,errno的常见取值为EAGAIN和EDEADLK,其中EAGAIN表示超出互斥锁递归锁定的最大次数,因此无法获取该互斥锁;EDEADLK表示当前线程已有互斥锁,二次加锁失败。

2.1.2 加锁

#include <pthread.h>int pthread_mutex_lock(pthread_mutex_t *mutex);

功能:锁定指定互斥量。

参数说明

  • mutex:待锁定的互斥量。

返回值说明

  • 成功:返回0;
  • 不成功:返回errno。
#include <pthread.h>int pthread_mutex_trylock(pthread_mutex_t *mutex);

功能:尝试加锁,若锁正在被使用,不阻塞等待,而是直接返回并返回错误号。

参数说明

  • mutex:待锁定的互斥量。

返回值说明

  • 成功:返回0;
  • 不成功:返回errno,其中常见的errno有两个,分别为EBUSY和EAGAIN,它们代表的含义如下:
    – EBUSY:参数mutex指向的互斥锁已锁定;
    – EAGAIN:超过互斥锁递归锁定的最大次数。

2.1.3 解锁

#include <pthread.h>int pthread_mutex_unlock(pthread_mutex_t *mutex);

功能:为指定互斥量解锁。

参数说明

  • mutex:待解锁的互斥量。

返回值说明

  • 成功:返回0;
  • 不成功:返回errno。

2.1.4 销毁锁

#include <pthread.h>int pthread_mutex_destroy(pthread_mutex_t *mutex);

功能:为指定互斥量销毁。

参数说明

  • mutex:待销毁的互斥量。

返回值说明

  • 成功:返回0;
  • 不成功:返回errno。

【案例 1】在原线程和新线程中分别进行打印操作,使原线程分别打印“HELLO”、“ WORLD”,新线程分别打印“hello”、“world”。

//未添加mutex
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
void *tfn(void *paraArg) {srand(time(NULL));while (1) {printf("hello ");//模拟长时间操作共享资源,导致cpu易主,产生与时间有关的错误sleep(rand() % 3);printf("world\n");sleep(rand() % 3);}//of whilereturn NULL;
}//of tfn
int main(void) {pthread_t tempTid;srand(time(NULL));pthread_create(&tempTid, NULL, tfn, NULL);while (1) {printf("HELLO ");sleep(rand() % 3);printf("WORLD\n");sleep(rand() % 3);}//of whilepthread_join(tempTid, NULL);return 0;
}//of main

未添加互斥量,会导致打印乱序。

#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
pthread_mutex_t globMutux;						//定义互斥锁
void err_thread(int paraRet, char *paraStr) {if (paraRet != 0) {fprintf(stderr, "%s:%s\n", paraStr, strerror(paraRet));pthread_exit(NULL);}//of if
}//of err_thread
void *tfn(void *paraArg) {srand(time(NULL));while (1) {pthread_mutex_lock(&globMutux);     		//加锁:m--printf("hello ");//模拟长时间操作共享资源,导致cpu易主,产生与时间有关的错误sleep(rand() % 3);printf("world\n");pthread_mutex_unlock(&globMutux);   		//解锁:m++sleep(rand() % 3);}//of whilereturn NULL;
}//of tfn
int main(void) {pthread_t tempTid;srand(time(NULL));int tempFlag = 5;pthread_mutex_init(&globMutux, NULL);        	//初始化mutex:m=1int tempRet = pthread_create(&tempTid, NULL, tfn, NULL);err_thread(tempRet, "pthread_create error");while (tempFlag--) {pthread_mutex_lock(&globMutux);     		//加锁:m--printf("HELLO ");sleep(rand() % 3);printf("WORLD\n");pthread_mutex_unlock(&globMutux);     	//解锁:m--sleep(rand() % 3);}//of whilepthread_cancel(tempTid);pthread_join(tempTid, NULL);pthread_mutex_destroy(&globMutux);return 0;
}//of main

2.2 条件变量

使用条件变量控制线程同步时,线程访问共享资源的前提,是程序中设置的条件变量得到满足。条件变量不会对共享资源加锁,但也会使线程阻塞,若线程不满足条件变量规定的条件,就会进入阻塞状态直到条件满足。
条件变量的使用分为以下四个步骤:

  • (1)初始化条件变量:pthread_cond_init();
  • (2)等待条件变量满足:pthread_cond_wait();
  • (3)唤醒阻塞线程:pthread_cond_signal()、pthread_cond_broadcast();
  • (4)释放条件变量:pthread_cond_destroy()。

2.2.1 初始化条件变量

#include <pthread.h>int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrict attr);

功能:初始化条件变量。

参数说明

  • cond:代表条件变量,一个指向pthread_cond_t的结构体指针,pthread_cond_t是Linux系统定义的条件变量类型;
  • attr:代表条件变量的属性:
    – NULL:表示使用默认属性初始化条件变量;
    – PTHREAD_PROCESS_PRIVATE:表示当前进程中的线程共用此条件变量;
    – PTHREAD_PROCESS_SHARED:表示多个进程间的线程共用条件变量。

返回值说明

  • 成功:返回0;
  • 不成功:返回-1并设置errno。

2.2.2 等待条件变量满足

#include <pthread.h>int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);

功能:使线程进入阻塞状态,等待一个条件变量后继续执行。pthread_cond_wait类似于互斥锁的pthread_mutex_lock函数,但其功能更为丰富,它的工作机制如下:

  • (1)阻塞等待条件变量cond满足;
  • (2)解除已绑定的互斥锁(类似于pthread_mutex_unlock);
  • (3)当线程被唤醒,pthread_cond_wait函数返回,pthread_cond_wait函数同时会解除线程阻塞,并使线程重新申请绑定互斥锁。
    在这里插入图片描述
    条件变量控制流程示意图

参数说明

  • cond:代表条件变量,一个指向pthread_cond_t的结构体指针,pthread_cond_t是Linux系统定义的条件变量类型;
  • mutex:代表与当前线程绑定的互斥锁。

返回值说明

  • 成功:返回0;
  • 不成功:返回-1并设置errno。
#include <pthread.h>int pthread_cond_timedwait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex,const struct timespec *restrict abstime);
struct timespec {time_t tv_sec;		//秒long tv_nsec;		//纳秒
};

功能:使线程阻塞等待条件变量,不同的是,该函数可以指定线程的阻塞时长,若等待超时,该函数便会返回。

参数说明

  • cond:代表条件变量,一个指向pthread_cond_t的结构体指针,pthread_cond_t是Linux系统定义的条件变量类型;
  • mutex:代表与当前线程绑定的互斥锁;
  • abstime:等待时长。

返回值说明

  • 成功:返回0;
  • 不成功:返回-1并设置errno。

2.2.3 唤醒条件变量

#include <pthread.h>int pthread_cond_signal(pthread_cond_t *cond);

功能:在条件变量满足之后,以信号的形式唤醒阻塞在该条件变量的一个线程。处于阻塞状态中的线程的唤醒顺序由调度策略决定。

参数说明

  • cond:代表条件变量,一个指向pthread_cond_t的结构体指针,pthread_cond_t是Linux系统定义的条件变量类型。

返回值说明

  • 成功:返回0;
  • 不成功:返回-1并设置errno。
#include <pthread.h>int pthread_cond_broadcast(pthread_cond_t *cond);

功能:唤醒阻塞在指定条件变量的线程,不同的是,该函数会以广播的形式,唤醒阻塞在该条件变量上的所有线程。

参数说明

  • cond:代表条件变量,一个指向pthread_cond_t的结构体指针,pthread_cond_t是Linux系统定义的条件变量类型。

返回值说明

  • 成功:返回0;
  • 不成功:返回-1并设置errno。

2.2.4 销毁条件变量

#include <pthread.h>int pthread_cond_destory(pthread_cond_t *cond);

功能:当没有线程在等待参数cond指定的条件变量时,才可以销毁条件变量。

参数说明

  • cond:代表条件变量,一个指向pthread_cond_t的结构体指针,pthread_cond_t是Linux系统定义的条件变量类型。

返回值说明

  • 成功:返回0;
  • 不成功:返回EBUSY。

【案例2】生产者-消费者模型是线程同步中的一个经典案例。假设有两个线程,这两个线程同时操作一个共享资源(一般称为汇聚),其中一个模拟生产者行为,生产共享资源,当容器存满时,生产者无法向其中放入产品;另一个线程模拟消费者行为,消费共享资源,当产品数量为0时,消费者无法获取产品,应阻塞等待。显然,为防止数据混乱,每次只能由生产者、消费者中的一个,操作共享资源。本案例要求使用程序实现简单的生产者-消费者模型(可假设容器无限大)

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
struct msg {struct msg *next;int num;
};
struct msg *globHead;
pthread_cond_t globHasProduct = PTHREAD_COND_INITIALIZER;	//初始化条件变量
pthread_mutex_t globLock = PTHREAD_MUTEX_INITIALIZER;		//初始化互斥锁
//消费者
void *consumer(void *paraP) {struct msg *tempMsgP;for (;;) {pthread_mutex_lock(&globLock);						//加锁//若头结点为空,表明产品数量为0,消费者无法消费产品while (globHead == NULL) {pthread_cond_wait(&globHasProduct, &globLock);		//阻塞等待并解锁}//of whiletempMsgP = globHead;globHead = tempMsgP->next;    					//模拟消费一个产品pthread_mutex_unlock(&globLock);printf("-Consume ---%d\n", tempMsgP->num);free(tempMsgP);sleep(rand() % 5);}//of for
}//of consumer
//生产者
void *producer(void *paraP) {struct msg *tempMsgP;while (1) {tempMsgP = malloc(sizeof(struct msg));tempMsgP->num = rand() % 1000 + 1;        	//模拟生产一个产品printf("-Produce ---%d\n", tempMsgP->num);pthread_mutex_lock(&globLock);				//加锁tempMsgP->next = globHead;					//插入结点(添加产品)globHead = tempMsgP;pthread_mutex_unlock(&globLock);				//解锁pthread_cond_signal(&globHasProduct);  		//唤醒等待在该条件变量上的一个线程sleep(rand() % 5);}//of while
}//of producer
int main(int argc, char *argv[]) {pthread_t tempPid, tempCid;srand(time(NULL));//创建生产者、消费者线程pthread_create(&tempPid, NULL, producer, NULL);pthread_create(&tempCid, NULL, consumer, NULL);//回收线程pthread_join(tempPid, NULL);pthread_join(tempCid, NULL);return 0;
}//of main

2.3 信号量

使用信号量实现线程同步时,线程在访问共享资源时会根据操作类型执行如下操作:

  • 若有线程申请访问共享资源,系统会执行P操作使共享资源计数减一;
  • 若有线程释放共享资源,系统会执行V操作使共享资源计数加一。

信号量的使用也分为四个步骤:

  • (1)初始化信号量:sem_init();
  • (2)阻塞等待信号量:sem_wait();
  • (3)唤醒阻塞线程:sem_post();
  • (4)释放信号量:sem_destroy()。

2.3.1 初始化信号量

#include <pthread.h>int sem_init(sem_t *sem, int pshared, unsigned int value);

功能:初始化信号量。

参数说明

  • sem:指向信号量变量的指针;
  • pshared:用于控制信号量的作用范围,其取值通常为0与非0:
    – 当pshared被设置为0时,信号量将会被放在进程中所有线程可见的地址内,由进程中的线程共享;
    – 当pshared被设置为非0值时,信号量将会被放置在共享内存区域,由所有进程共享。
  • value:设置信号量sem的初值。

返回值说明

  • 成功:返回0;
  • 不成功:返回-1,并设置errno。

2.3.2 阻塞等待信号量

#include <pthread.h>int sem_wait(sem_t *sem);

功能:阻塞等待信号量,sem_wait函数对应P操作,若调用成功,则会使信号量sem的值减一。

参数说明

  • sem:指向信号量变量的指针。

返回值说明

  • 成功:返回0;
  • 不成功:返回-1,并设置errno。

2.3.3 唤醒阻塞线程

#include <pthread.h>int sem_post(sem_t *sem);

功能:唤醒阻塞线程,sem_post函数对应V操作,若调用成功,则会使信号量sem的值加一。

参数说明

  • sem:指向信号量变量的指针。

返回值说明

  • 成功:返回0;
  • 不成功:返回-1,并设置errno。

2.3.4 释放信号量

#include <pthread.h>int sem_destroy(sem_t *sem);

功能:与互斥锁类似,信号量也是一种系统资源,使用完毕之后应主动回收,函数调用成功,则会使信号量sem的值加一。

参数说明

  • sem:指向信号量变量的指针。

返回值说明

  • 成功:返回0;
  • 不成功:返回-1,并设置errno。

2.3.5 获取信号量

#include <pthread.h>int sem_getvalue(sem_t *sem, int *sval);

功能:获取系统中当前信号量的值。

参数说明

  • sem:指向信号量变量的指针;
  • sval:个传入指针,用于获取信号量的值,信号量sem的值会被存储在参数sval中。

返回值说明

  • 成功:返回0;
  • 不成功:返回-1,并设置errno。

【案例 3】实现一个模拟生产者-消费者模型,但对生产者进行限制:若容器已满,生产者不能生产,需等待消费者消费。

#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <stdio.h>
#include <semaphore.h>
#define NUM 5 
int globQueue[NUM];                         //全局数组实现环形队列
sem_t globBlankNum, globProductNum;   		//空格子信号量, 产品信号量
void *producer(void *paraArg) {int i = 0;while (1) {sem_wait(&globBlankNum);       		//生产者将空格子数--,为0则阻塞等待globQueue[i] = rand() % 1000 + 1;   //生产一个产品printf("----Produce---%d\n", globQueue[i]);sem_post(&globProductNum);          //将产品数++i = (i + 1) % NUM;                  //借助下标实现环形sleep(rand() % 1);}//of while
}//of producer
void *consumer(void *paraArg) {int i = 0;while (1) {sem_wait(&globProductNum); 		//消费者将产品数--,为0则阻塞等待printf("-Consume---%d\t%lu\n", globQueue[i], pthread_self());globQueue[i] = 0;               //消费一个产品 sem_post(&globBlankNum);        //消费掉以后,将空格子数++i = (i + 1) % NUM;sleep(rand() % 1);}//of while
}//of consumer
int main(int paraArgc, char *paraArgv[]) {pthread_t tempPid, tempCid;sem_init(&globBlankNum, 0, NUM);    //初始化空格子信号量为5sem_init(&globProductNum, 0, 0);    //初始化产品数信号量为0pthread_create(&tempPid, NULL, producer, NULL);pthread_create(&tempCid, NULL, consumer, NULL);pthread_create(&tempCid, NULL, consumer, NULL);pthread_join(tempPid, NULL);pthread_join(tempCid, NULL);sem_destroy(&globBlankNum);sem_destroy(&globProductNum);return 0;
}//of main

2.4 小结

本部分主要讲解了Linux系统中与线程相关的知识,包括线程相关操作及线程同步,其中线程操作包括创建线程、退出线程、终止线程、回收线程等;线程同步包括互斥锁、条件变量、信号量这线程同步的三种方式。线程是Linux编程基础中非常重要的一项内容。

2.5 编程题

【1】编写一个程序,开启三个线程,第一个线程向终端输出A,第二个线程向终端输出B,第三个线程向终端输出C,每个线程打印10遍,要求输出必须按照ABC的顺序显示,如:ABCABCABC…
【2】利用线程的信号量实现互斥功能,模拟打印机。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/507408.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

电脑开两个微信_电脑怎么登录两个微信

1/4下载并安装微信电脑客户端&#xff0c;保证这台电脑没有登陆微信2/4就像正常打开微信一样&#xff0c;不过不是双击&#xff0c;而是快速连点四次3/4我们可以看到有两个微信登陆界面4/4用两个不同的账号进行扫码登陆即可

idea 查询项目代码行数_idea统计代码行数Statistic的步骤详解

idea统计代码行数Statistic的步骤详解idea统计代码行数可以用到插件&#xff1a;Statistic。步骤&#xff1a;File→Settings进入Plugins点击Marketplace搜索Statistic 安装蓝框标出的插件重启idea后就可以看到效果了(图是拿的别人的&#xff0c;基本就是这效果)如果没有下…

Linux编程基础 7.1:套接字通信流程及编程接口

1 socket通信流程 2 socket编程接口 Linux系统中常用的socket网络编程接口有&#xff1a; socket()bind()listen()accept()connect()send()recv()close()其中connect()与send()为客户端专用接口&#xff1b;bind()、listen()、accept()及recv()为服务器端专用接口&#xff1b…

cad2016中选择全图字体怎么操作_cad教程分享CAD中如何删除顽固图层?

Autocad教程公众号&#xff0c;专注于cad教程、cad教程视频的分享&#xff0c;欢迎关注&#xff0c;下载你所需的教程资源&#xff01;如你还未关注&#xff0c;请点击文章标题下方蓝色字体的"Autocad教程"进行关注。cad教程分享-CAD中如何删除顽固图层&#xff1f;方…

python开启多个端口服务_python bottle使用多个端口(多个进程)提高并发

我的程序是用python结合bottle框架写的&#xff0c;但bottle自带wsgi原本只是单进程单线程运行模式(Bottle 默认运行在内置的 wsgiref 服务器上面。这个单线程的 HTTP 服务器在开发的时候特别有用&#xff0c;但其性能低下&#xff0c;在服务器负载不断增加的时候也许会是性能瓶…

Linux编程基础 7.2:服务器和客户端编程案例

1 网络字节序 大端模式&#xff1a;若将数据的高字节保存在内存的低地址&#xff0c;将数据的低字节保存在内存的高地址&#xff1b; 小端模式&#xff1a;若将数据的高字节保存在内存的高地址&#xff0c;将数据的低字节保存在内存的低地址。 网络数据流&#xff1a;大端模式…

div 隐藏_div的position属性

如果你想把div放到合适的位置&#xff0c;请看看这篇文章。<!-- div的position属性--><html><style>.red{height:100px;background:red;}.green{height:100px;background:green;position:relative;left:50px;top:50px;}.black{height:100px;background:black…

苹果笔记本python怎么换行_python怎么换行,我的换行就是执行啊

展开全部首先运行终端或者cmd命令行(windows下)。e69da5e887aa62616964757a686964616f31333433646338执行python3.5的命令。然后输入如下图所示的内容。这种换行方法也可以在编辑器中进行&#xff0c;这里以vim为例&#xff0c;输入与上图类似的代码&#xff0c;保存为t.py脚本…

Linux编程基础 7.3:套接字本地通信

1 socket本地通信 socket原本是为网络通讯设计的&#xff0c;但后来在socket框架的基础上发展出了一种IPC&#xff08;进程通信&#xff09;机制&#xff0c;即UNIX Domain Socket&#xff0c;专门用来实现使用socket实现的本地进程通信。 本地通信的流程与使用的接口与基于TC…

乔布斯在斯坦福大学演讲稿英文_西方大文豪最爱的10个英文单词,写尽人世间细腻情感!...

从小浸染在汉语中的我们&#xff0c;常被汉字的意象美震撼到&#xff0c;一字就是一世界。汉字有种无与伦比的美丽&#xff0c;寥寥数字就能营造“只可意会不可言传”的意境&#xff0c;很多人感慨英文就是一串拉丁字母&#xff0c;无法传递细腻的情感。比如很多人说像「缘分」…

dorado 刷新_dorado7常用内容

tabControl").set("currentTab","tab2");// 通过tab的索引(index)属性切换view.get("#tabControl").set("currentIndex",1);//根据名字切换tabvar tab self.get("currentTab").get("name");if(tab!"ta…

web前端开发论文写作_外语论文文献引言格式—MLA Style

我们之前讨论了外语论文文献引用格式—APA Style和Chicago Style—芝加哥论文脚注引注格式&#xff0c;今天我们来介绍在Essay写作中如何使用APA格式引用文献。MLA格式是英文论文写作最常用的一种参考文献格式。很多留学小伙伴都觉得MLA引用格式很复杂&#xff0c;今天译然小编…

Linux编程基础--目录

本系列教程以《Linux编程基础》为基础&#xff0c;希望在大家的帮助下&#xff0c;逐步完善Linux下系统级程序设计。 1 初识Linux 1.1 课程简介及Linux介绍 2 Linux文件操作 2.1 文件I/O 2.2 文件操作 3 进程管理 3.1 进程控制 3.2 exec家族 3.3 进程同步 4 信号 4.1 系…

100999凑整到万位进一_四年级数学专项练习

填空题1、当除数是34时&#xff0c;试商时可以把除数看作( )&#xff0c;这样初商容易偏( )。2、()个26相加的和是468&#xff1b;()比12个15多20。3、34&#xff1d;21……&#xff0c;余数最大是()&#xff0c;这时被除数是()。4、在括号里填上合适的数。480秒()分540厘米()分…

cosx等价无穷小_第一章 函数与极限 第七节 无穷小的比较

我的公众号“每日晴天”&#xff0c;可关注领取我的笔记pdf版哦~------------------------------------------------------------------------------本文主要首先把书上的定义和知识点总结起来&#xff0c;方便复习要点背诵&#xff0c;同时在最后分割线之后补充自己当时在学习…

Linux编程基础 8.1:多进程并发服务器

多进程并发服务器 多线程并发服务器 I/O多路转接服务器 epoll的工作模式 1 多进程并发服务器 在多进程并发服务器中&#xff0c;若有用户请求到达&#xff0c;服务器将会调用fork()函数&#xff0c;创建一个子进程&#xff0c;之后父进程将继续调用accept()&#xff0c;而子进…

pmbok第七版_PMBOK第七版要来了!都有哪些变化?你准备好了么?

PMBOK第7版#PMP##职场##项目管理##战略##价值#2020年1月15日PMBOK 第7版的征求意见稿发布&#xff0c;并于2020年1月14日结束意见征集&#xff0c;预计于今年第四季度发布。基于我的项目组合管理(PfMP)&#xff0c;项目集管理(PgMP),项目管理(PMP)的培训和研究经验&#xff0c;…

Linux编程基础 8.2:多线程并发服务器

2 多线程并发服务器 每个进程可打开的文件描述符数量有限&#xff0c;且进程占用资源较多&#xff0c;系统中进程的数量又受到内存大小的限制&#xff0c;为保证服务器效率&#xff0c;降低服务器消耗&#xff0c;可利用多线程机制搭建并发服务器。 【案例2】搭建多线程并发服…

简述python的特性_Python的特性概要

1、和C比较&#xff0c;Python是解释型的语言&#xff0c; 2、a>字节码特性 b>动态语义&#xff0c;即在赋值时才确定数据类型 c>缩进&#xff0c;点击tap键缩进四个空格&#xff0c;使用编译器是记得查看&#xff01;3、注意 写Python的时候首先要记得定义编码格式&a…

android netty导入_Android Netty框架的使用

public abstract class BaseClientMgr extends Subject implementsIClientConnect {protected boolean isRunning; //当前是否正在连接protected boolean isSending; //是否正在发送 线程是否被占用private int mPort; //连接服务器的端口号private int mCommunication; //通讯…