python线程状态_Python线程

1. 线程基础

1.1. 线程状态

线程有5种状态,状态转换的过程如下图所示:

5d69877b9a10ce497ca0b6293838fcf1.png

1.2. 线程同步(锁)

多线程的优势在于可以同时运行多个任务(至少感觉起来是这样)。但是当线程需要共享数据时,可能存在数据不同步的问题。考虑这样一种情况:一个列表里所有元素都是0,线程”set”从后向前把所有元素改成1,而线程”print”负责从前往后读取列表并打印。那么,可能线程”set”开始改的时候,线程”print”便来打印列表了,输出就成了一半0一半1,这就是数据的不同步。为了避免这种情况,引入了锁的概念。

锁有两种状态——锁定和未锁定。每当一个线程比如”set”要访问共享数据时,必须先获得锁定;如果已经有别的线程比如”print”获得锁定了,那么就让线程”set”暂停,也就是同步阻塞;等到线程”print”访问完毕,释放锁以后,再让线程”set”继续。经过这样的处理,打印列表时要么全部输出0,要么全部输出1,不会再出现一半0一半1的尴尬场面。

线程与锁的交互如下图所示:

f413e4ff3a93ece9be0aa08d07593cf4.png

1.3. 线程通信(条件变量)

然而还有另外一种尴尬的情况:列表并不是一开始就有的;而是通过线程”create”创建的。如果”set”或者”print” 在”create”还没有运行的时候就访问列表,将会出现一个异常。使用锁可以解决这个问题,但是”set”和”print”将需要一个无限循环——他们不知道”create”什么时候会运行,让”create”在运行后通知”set”和”print”显然是一个更好的解决方案。于是,引入了条件变量。

条件变量允许线程比如”set”和”print”在条件不满足的时候(列表为None时)等待,等到条件满足的时候(列表已经创建)发出一个通知,告诉”set” 和”print”条件已经有了,你们该起床干活了;然后”set”和”print”才继续运行。

线程与条件变量的交互如下图所示:

9c73c204ba88bc7251cd421889e405a2.png

46c377e23dc67004e61da7c388c617c4.png

1.4. 线程运行和阻塞的状态转换

最后看看线程运行和阻塞状态的转换。

32fb107530974922e21b80d69d64bffe.png

阻塞有三种情况:

同步阻塞是指处于竞争锁定的状态,线程请求锁定时将进入这个状态,一旦成功获得锁定又恢复到运行状态;

等待阻塞是指等待其他线程通知的状态,线程获得条件锁定后,调用“等待”将进入这个状态,一旦其他线程发出通知,线程将进入同步阻塞状态,再次竞争条件锁定;

而其他阻塞是指调用time.sleep()、anotherthread.join()或等待IO时的阻塞,这个状态下线程不会释放已获得的锁定。

tips: 如果能理解这些内容,接下来的主题将是非常轻松的;并且,这些内容在大部分流行的编程语言里都是一样的。(意思就是非看懂不可 >_< 嫌作者水平低找别人的教程也要看懂)

2. thread

Python通过两个标准库thread和threading提供对线程的支持。thread提供了低级别的、原始的线程以及一个简单的锁。

Python

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

# encoding: UTF-8

importthread

importtime

# 一个用于在线程中执行的函数

deffunc():

foriinrange(5):

print'func'

time.sleep(1)

# 结束当前线程

# 这个方法与thread.exit_thread()等价

thread.exit()# 当func返回时,线程同样会结束

# 启动一个线程,线程立即开始运行

# 这个方法与thread.start_new_thread()等价

# 第一个参数是方法,第二个参数是方法的参数

thread.start_new(func,())# 方法没有参数时需要传入空tuple

# 创建一个锁(LockType,不能直接实例化)

# 这个方法与thread.allocate_lock()等价

lock=thread.allocate()

# 判断锁是锁定状态还是释放状态

printlock.locked()

# 锁通常用于控制对共享资源的访问

count=0

# 获得锁,成功获得锁定后返回True

# 可选的timeout参数不填时将一直阻塞直到获得锁定

# 否则超时后将返回False

iflock.acquire():

count+=1

# 释放锁

lock.release()

# thread模块提供的线程都将在主线程结束后同时结束

time.sleep(6)

thread 模块提供的其他方法:

thread.interrupt_main(): 在其他线程中终止主线程。

thread.get_ident(): 获得一个代表当前线程的魔法数字,常用于从一个字典中获得线程相关的数据。这个数字本身没有任何含义,并且当线程结束后会被新线程复用。

thread还提供了一个ThreadLocal类用于管理线程相关的数据,名为 thread._local,threading中引用了这个类。

由于thread提供的线程功能不多,无法在主线程结束后继续运行,不提供条件变量等等原因,一般不使用thread模块,这里就不多介绍了。

3. threading

threading基于Java的线程模型设计。锁(Lock)和条件变量(Condition)在Java中是对象的基本行为(每一个对象都自带了锁和条件变量),而在Python中则是独立的对象。Python Thread提供了Java Thread的行为的子集;没有优先级、线程组,线程也不能被停止、暂停、恢复、中断。Java Thread中的部分被Python实现了的静态方法在threading中以模块方法的形式提供。

threading 模块提供的常用方法:

threading.currentThread(): 返回当前的线程变量。

threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。

threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。

threading模块提供的类:

Thread, Lock, Rlock, Condition, [Bounded]Semaphore, Event, Timer, local.

3.1. Thread

Thread是线程类,与Java类似,有两种使用方法,直接传入要运行的方法或从Thread继承并覆盖run():

Python

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

# encoding: UTF-8

importthreading

# 方法1:将要执行的方法作为参数传给Thread的构造方法

deffunc():

print'func() passed to Thread'

t=threading.Thread(target=func)

t.start()

# 方法2:从Thread继承,并重写run()

classMyThread(threading.Thread):

defrun(self):

print'MyThread extended from Thread'

t=MyThread()

t.start()

构造方法:

Thread(group=None, target=None, name=None, args=(), kwargs={})

group: 线程组,目前还没有实现,库引用中提示必须是None;

target: 要执行的方法;

name: 线程名;

args/kwargs: 要传入方法的参数。

实例方法:

isAlive(): 返回线程是否在运行。正在运行指启动后、终止前。

get/setName(name): 获取/设置线程名。

is/setDaemon(bool): 获取/设置是否守护线程。初始值从创建该线程的线程继承。当没有非守护线程仍在运行时,程序将终止。

start(): 启动线程。

join([timeout]): 阻塞当前上下文环境的线程,直到调用此方法的线程终止或到达指定的timeout(可选参数)。

一个使用join()的例子:

Python

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

# encoding: UTF-8

importthreading

importtime

defcontext(tJoin):

print'in threadContext.'

tJoin.start()

# 将阻塞tContext直到threadJoin终止。

tJoin.join()

# tJoin终止后继续执行。

print'out threadContext.'

defjoin():

print'in threadJoin.'

time.sleep(1)

print'out threadJoin.'

tJoin=threading.Thread(target=join)

tContext=threading.Thread(target=context,args=(tJoin,))

tContext.start()

运行结果:

in threadContext.

in threadJoin.

out threadJoin.

out threadContext.

3.2. Lock

Lock(指令锁)是可用的最低级的同步指令。Lock处于锁定状态时,不被特定的线程拥有。Lock包含两种状态——锁定和非锁定,以及两个基本的方法。

可以认为Lock有一个锁定池,当线程请求锁定时,将线程至于池中,直到获得锁定后出池。池中的线程处于状态图中的同步阻塞状态。

构造方法:

Lock()

实例方法:

acquire([timeout]): 使线程进入同步阻塞状态,尝试获得锁定。

release(): 释放锁。使用前线程必须已获得锁定,否则将抛出异常。

Python

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

# encoding: UTF-8

importthreading

importtime

data=0

lock=threading.Lock()

deffunc():

globaldata

print'%s acquire lock...'%threading.currentThread().getName()

# 调用acquire([timeout])时,线程将一直阻塞,

# 直到获得锁定或者直到timeout秒后(timeout参数可选)。

# 返回是否获得锁。

iflock.acquire():

print'%s get the lock.'%threading.currentThread().getName()

data+=1

time.sleep(2)

print'%s release lock...'%threading.currentThread().getName()

# 调用release()将释放锁。

lock.release()

t1=threading.Thread(target=func)

t2=threading.Thread(target=func)

t3=threading.Thread(target=func)

t1.start()

t2.start()

t3.start()

3.3. RLock

RLock(可重入锁)是一个可以被同一个线程请求多次的同步指令。RLock使用了“拥有的线程”和“递归等级”的概念,处于锁定状态时,RLock被某个线程拥有。拥有RLock的线程可以再次调用acquire(),释放锁时需要调用release()相同次数。

可以认为RLock包含一个锁定池和一个初始值为0的计数器,每次成功调用 acquire()/release(),计数器将+1/-1,为0时锁处于未锁定状态。

构造方法:

RLock()

实例方法:

acquire([timeout])/release(): 跟Lock差不多。

Python

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

# encoding: UTF-8

importthreading

importtime

rlock=threading.RLock()

deffunc():

# 第一次请求锁定

print'%s acquire lock...'%threading.currentThread().getName()

ifrlock.acquire():

print'%s get the lock.'%threading.currentThread().getName()

time.sleep(2)

# 第二次请求锁定

print'%s acquire lock again...'%threading.currentThread().getName()

ifrlock.acquire():

print'%s get the lock.'%threading.currentThread().getName()

time.sleep(2)

# 第一次释放锁

print'%s release lock...'%threading.currentThread().getName()

rlock.release()

time.sleep(2)

# 第二次释放锁

print'%s release lock...'%threading.currentThread().getName()

rlock.release()

t1=threading.Thread(target=func)

t2=threading.Thread(target=func)

t3=threading.Thread(target=func)

t1.start()

t2.start()

t3.start()

3.4. Condition

Condition(条件变量)通常与一个锁关联。需要在多个Contidion中共享一个锁时,可以传递一个Lock/RLock实例给构造方法,否则它将自己生成一个RLock实例。

可以认为,除了Lock带有的锁定池外,Condition还包含一个等待池,池中的线程处于状态图中的等待阻塞状态,直到另一个线程调用notify()/notifyAll()通知;得到通知后线程进入锁定池等待锁定。

构造方法:

Condition([lock/rlock])

实例方法:

acquire([timeout])/release(): 调用关联的锁的相应方法。

wait([timeout]): 调用这个方法将使线程进入Condition的等待池等待通知,并释放锁。使用前线程必须已获得锁定,否则将抛出异常。

notify(): 调用这个方法将从等待池挑选一个线程并通知,收到通知的线程将自动调用acquire()尝试获得锁定(进入锁定池);其他线程仍然在等待池中。调用这个方法不会释放锁定。使用前线程必须已获得锁定,否则将抛出异常。

notifyAll(): 调用这个方法将通知等待池中所有的线程,这些线程都将进入锁定池尝试获得锁定。调用这个方法不会释放锁定。使用前线程必须已获得锁定,否则将抛出异常。

例子是很常见的生产者/消费者模式:

Python

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

# encoding: UTF-8

importthreading

importtime

# 商品

product=None

# 条件变量

con=threading.Condition()

# 生产者方法

defproduce():

globalproduct

ifcon.acquire():

whileTrue:

ifproduct isNone:

print'produce...'

product='anything'

# 通知消费者,商品已经生产

con.notify()

# 等待通知

con.wait()

time.sleep(2)

# 消费者方法

defconsume():

globalproduct

ifcon.acquire():

whileTrue:

ifproduct isnotNone:

print'consume...'

product=None

# 通知生产者,商品已经没了

con.notify()

# 等待通知

con.wait()

time.sleep(2)

t1=threading.Thread(target=produce)

t2=threading.Thread(target=consume)

t2.start()

t1.start()

3.5. Semaphore/BoundedSemaphore

Semaphore(信号量)是计算机科学史上最古老的同步指令之一。Semaphore管理一个内置的计数器,每当调用acquire()时-1,调用release() 时+1。计数器不能小于0;当计数器为0时,acquire()将阻塞线程至同步锁定状态,直到其他线程调用release()。

基于这个特点,Semaphore经常用来同步一些有“访客上限”的对象,比如连接池。

BoundedSemaphore 与Semaphore的唯一区别在于前者将在调用release()时检查计数器的值是否超过了计数器的初始值,如果超过了将抛出一个异常。

构造方法:

Semaphore(value=1): value是计数器的初始值。

实例方法:

acquire([timeout]): 请求Semaphore。如果计数器为0,将阻塞线程至同步阻塞状态;否则将计数器-1并立即返回。

release(): 释放Semaphore,将计数器+1,如果使用BoundedSemaphore,还将进行释放次数检查。release()方法不检查线程是否已获得 Semaphore。

Python

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

# encoding: UTF-8

importthreading

importtime

# 计数器初值为2

semaphore=threading.Semaphore(2)

deffunc():

# 请求Semaphore,成功后计数器-1;计数器为0时阻塞

print'%s acquire semaphore...'%threading.currentThread().getName()

ifsemaphore.acquire():

print'%s get semaphore'%threading.currentThread().getName()

time.sleep(4)

# 释放Semaphore,计数器+1

print'%s release semaphore'%threading.currentThread().getName()

semaphore.release()

t1=threading.Thread(target=func)

t2=threading.Thread(target=func)

t3=threading.Thread(target=func)

t4=threading.Thread(target=func)

t1.start()

t2.start()

t3.start()

t4.start()

time.sleep(2)

# 没有获得semaphore的主线程也可以调用release

# 若使用BoundedSemaphore,t4释放semaphore时将抛出异常

print'MainThread release semaphore without acquire'

semaphore.release()

3.6. Event

Event(事件)是最简单的线程通信机制之一:一个线程通知事件,其他线程等待事件。Event内置了一个初始为False的标志,当调用set()时设为True,调用clear()时重置为 False。wait()将阻塞线程至等待阻塞状态。

Event其实就是一个简化版的 Condition。Event没有锁,无法使线程进入同步阻塞状态。

构造方法:

Event()

实例方法:

isSet(): 当内置标志为True时返回True。

set(): 将标志设为True,并通知所有处于等待阻塞状态的线程恢复运行状态。

clear(): 将标志设为False。

wait([timeout]): 如果标志为True将立即返回,否则阻塞线程至等待阻塞状态,等待其他线程调用set()。

Python

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

# encoding: UTF-8

importthreading

importtime

event=threading.Event()

deffunc():

# 等待事件,进入等待阻塞状态

print'%s wait for event...'%threading.currentThread().getName()

event.wait()

# 收到事件后进入运行状态

print'%s recv event.'%threading.currentThread().getName()

t1=threading.Thread(target=func)

t2=threading.Thread(target=func)

t1.start()

t2.start()

time.sleep(2)

# 发送事件通知

print'MainThread set event.'

event.set()

3.7. Timer

Timer(定时器)是Thread的派生类,用于在指定时间后调用一个方法。

构造方法:

Timer(interval, function, args=[], kwargs={})

interval: 指定的时间

function: 要执行的方法

args/kwargs: 方法的参数

实例方法:

Timer从Thread派生,没有增加实例方法。

Python

1

2

3

4

5

6

7

8

# encoding: UTF-8

importthreading

deffunc():

print'hello timer!'

timer=threading.Timer(5,func)

timer.start()

3.8. local

local是一个小写字母开头的类,用于管理 thread-local(线程局部的)数据。对于同一个local,线程无法访问其他线程设置的属性;线程设置的属性不会被其他线程设置的同名属性替换。

可以把local看成是一个“线程-属性字典”的字典,local封装了从自身使用线程作为 key检索对应的属性字典、再使用属性名作为key检索属性值的细节。

Python

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

# encoding: UTF-8

importthreading

local=threading.local()

local.tname='main'

deffunc():

local.tname='notmain'

printlocal.tname

t1=threading.Thread(target=func)

t1.start()

t1.join()

printlocal.tname

熟练掌握Thread、Lock、Condition就可以应对绝大多数需要使用线程的场合,某些情况下local也是非常有用的东西。本文的最后使用这几个类展示线程基础中提到的场景:

Python

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

# encoding: UTF-8

importthreading

alist=None

condition=threading.Condition()

defdoSet():

ifcondition.acquire():

whilealist isNone:

condition.wait()

foriinrange(len(alist))[::-1]:

alist[i]=1

condition.release()

defdoPrint():

ifcondition.acquire():

whilealist isNone:

condition.wait()

foriinalist:

printi,

print

condition.release()

defdoCreate():

globalalist

ifcondition.acquire():

ifalist isNone:

alist=[0foriinrange(10)]

condition.notifyAll()

condition.release()

tset=threading.Thread(target=doSet,name='tset')

tprint=threading.Thread(target=doPrint,name='tprint')

tcreate=threading.Thread(target=doCreate,name='tcreate')

tset.start()

tprint.start()

tcreate.start()

全文完

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

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

相关文章

JavaScript中错误正确处理方式,你用对了吗?

JavaScript的事件驱动范式增添了丰富的语言&#xff0c;也是让使用JavaScript编程变得更加多样化。如果将浏览器设想为JavaScript的事件驱动工具&#xff0c;那么当错误发生时&#xff0c;某个事件就会被抛出。理论上可以认为这些发生的错误只是JavaScript中的简单事件。 本文将…

文件分割机

文件分割与合并 要求&#xff1a;实现对大文件的分割与合并。 按指定个数切&#xff08;如把一个文件切成10份&#xff09;或按指定大小切&#xff08;如每份最大不超过10M&#xff09;&#xff0c;这两种方式都能够。 程序说明&#xff1a; 文件分割&#xff1a;把一个文件分割…

mysql pow函数怎么用_pow函数怎么用

PHP pow函数表示指数表达式。pow函数怎么用&#xff1f;php pow()函数 语法作用&#xff1a;pow()函数的作用是将一个数进行n次方计算后返回语法&#xff1a;pow(X,Y);参数&#xff1a;X表示要做处理的数字Y表示指定n次方中的n数值说明&#xff1a;返回X的Y次方幂&#xff0c;如…

【IntelliJ IDEA】添加一个新的tomcat,tomcat启动无法访问欢迎页面,空白页,404

第一部分&#xff0c;添加一个tomcat 1.先把Toolbar 和 Tool Buttons显示出来 2.选择Edit Configurations 3.添加一个新的tomcat server进来 解决no artifacts configured问题&#xff0c;就是没有项目加入里面 先添加一个没有的项目 启动之后找不到 页面 第二部分&#xff0c…

mac eclipse tomcat mysql_mac下使用eclipse+tomcat+mysql开发 j2ee(一)

在这里是使用eclipse&#xff0b;tomcat&#xff0b;mysql的方式。1、软件下载(1) eclipse我使用的是mac&#xff0c;因为本人是苹果客户端开发者&#xff0c;所以目前只有苹果机器了。首先下载eclipse for mac&#xff0c;选择mac 64位&#xff0c;下载eclipse-jee-kepler-SR1…

社交系统ThinkSNS-plus(TS+)V1.0发布!

2019独角兽企业重金招聘Python工程师标准>>> 需要购买源码的同志们&#xff0c;依然随时可以联系我们手机&#xff1a;18108035545&#xff08;同微信&#xff09;&#xff1b;电话&#xff1a;028-82884828 &#xff1b;QQ&#xff1a;3298713109&#xff1b; 从决…

.net 读取mysql数据库配置文件_.NETCore添加及读取默认配置文件信息

这里的默认配置文件指的是项目中的appsettings.json文件&#xff0c;我们可以把一些配置信息保存在里面&#xff0c;比如数据库连接信息。来吧&#xff0c;看例子&#xff1a;1、先在appsettings.json文件中写入数据库的连接信息&#xff1a;{"Logging": {"LogL…

h5 img js 点击图片放大_H5实现移动端图片预览:手势缩放, 手势拖动,双击放大......

查看示例效果&#xff1a;一、功能介绍图片预览主要有以下几个功能点组成&#xff1a;监听图片点击事件&#xff0c;进入图片预览模式自定义手势事件, (双指缩放&#xff0c;滑动&#xff0c;双击。。。)监听图片手势事件&#xff0c;通过 transform-matrix 实现图片的各种变换…

[转载]建立团队沟通协作工作方式

很多初创团队、以及刚开始尝试敏捷的团队&#xff0c;没有工作协议的概念&#xff0c;热热闹闹&#xff0c;混混乱乱。本文介绍了关于工作协议的What, Why, Who, When, 以及How。 What:什么是工作协议 工作协议&#xff1a;由团队共同商议&#xff0c;达成一致遵守的一组规则、…

linux安装自带mysql吗_Linux下安装mysql

前提下必须要有这三个文件夹A.jpgB.jpg还要安装这两个软件&#xff1b;直接百度官网即可&#xff1b;先通过Xftp6这个软件&#xff0c;编译文件夹&#xff0c;C.jpg搭建mysql1.查看CentOS自带的mysqlrpm -qa | grep mysql2.卸载CentOS自带的mysqlrpm -e --nodeps 要卸载的软件3…

(转载)UI接口分层自动化测试框架设计思想

阅读本小节&#xff0c;需要读者具备如下前提条件&#xff1a; 1. 掌握一种编程语言基础&#xff0c;如java、python等。 2. 掌握一种单元测试框架&#xff0c;如java语言的testng框架、python的unittest框架。 3. 掌握目前主流的UI测试框架&#xff0c;移动端APP测试框架Ap…

170821-关于SpringMVC的知识点

1.SpringMVC 概述以及优势 SpringMVC和Spring的关系&#xff1a; 软件开发的三层架构&#xff1a; web层【表示层、表现层】---->Service层---->Dao[DataBase Access Object]---->数据库&#xff01; SpringMVC实际上是Spring的一个子模块&#xff0c;我们用Spring…

写在前面的一些话:《Learning OpenCV》中文版 .

2009-09-17 15:51 7578人阅读 评论(4) 收藏 举报 <!-- /* Font Definitions */ font-face {font-family:Helvetica; panose-1:2 11 5 4 2 2 2 2 2 4; mso-font-charset:0; mso-generic-font-family:swiss; mso-font-format:other; mso-font-pitch:variable; mso-font-sign…

独家 | 一文读懂自然语言处理NLP(附学习资料)

前言 自然语言处理是文本挖掘的研究领域之一&#xff0c;是人工智能和语言学领域的分支学科。在此领域中探讨如何处理及运用自然语言。 对于自然语言处理的发展历程&#xff0c;可以从哲学中的经验主义和理性主义说起。基于统计的自然语言处理是哲学中的经验主义&#xff0c;基…

GNU/Linux下有多少是GNU的?

导读&#xff1a;一个葡萄牙的学生写了一篇文章 《How much GNU is there in GNU/Linux?》由酷壳网的陈皓整理编译为《GNU/Linux下有多少是GNU的》。这篇文章主要分布了今年4月份的Ubuntu Natty的Linux分发包。其主要是用代码行来做的分析&#xff0c;用两个饼图对比分析。 内…

Unity 富文本

参考链接&#xff1a;http://www.ceeger.com/Manual/StyledText.html 首先要说的是不仅仅ugui的text组件支持富文本&#xff0c;Debug.Log也是支持的 Debug.Log("<color#ffff00ff><b>爱生活</b></color> <color#00ffffff><b> 爱海澜&…

Linux shell 之 提取文件名和目录名的一些方法

很多时候在使用Linux的shell时&#xff0c;我们都需要对文件名或目录名进行处理&#xff0c;通常的操作是由路径中提取出文件名&#xff0c;从路径中提取出目录名&#xff0c;提取文件后缀名等等。例如&#xff0c;从路径/dir1/dir2/file.txt中提取也文件名file.txt&#xff0c…

bzoj 2752: [HAOI2012]高速公路(road)

Description Y901高速公路是一条重要的交通纽带&#xff0c;政府部门建设初期的投入以及使用期间的养护费用都不低&#xff0c;因此政府在这条高速公路上设立了许多收费站。Y901高速公路是一条由N-1段路以及N个收费站组成的东西向的链&#xff0c;我们按照由西向东的顺序将收费…

长沙java技术_长沙如何提高自身的Java技术

长沙如何提高自身的Java技术&#xff1f;Java自发行二十多年来&#xff0c;一直都是开发者的宠儿&#xff0c;在编程界的位置一直十分稳固。虽然Java人才需求量大&#xff0c;薪资水平高&#xff0c;但想要用Java语言胜任企业工作不容易。比如要成为一名Java架构师&#xff0c;…

互联网

2019独角兽企业重金招聘Python工程师标准>>> 转载于:https://my.oschina.net/u/3127489/blog/1550726