Skip to content

Commit e9e59c4

Browse files
committed
ex132
1 parent 61ab621 commit e9e59c4

19 files changed

Lines changed: 551 additions & 1 deletion

File tree

README.md

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,5 +149,22 @@
149149
| 112 | [判断月有几天](md/112.md) | calendar,datetime | V1.0 | ⭐️⭐️⭐️ |
150150
| 113 | [月的第一天](md/113.md) | datetime | V1.0 | ⭐️⭐️ |
151151
| 114 | [月的最后一天](md/114.md) | calendar,datetime | V1.0 | ⭐️⭐️ |
152-
152+
| 115 | [获取当前时间](md/115.md) | time,datetime | V1.0 | ⭐️⭐️ |
153+
| 116 | [字符时间转时间](md/116.md) | time,datetime | V1.0 | ⭐️⭐️ |
154+
| 117 | [时间转字符时间](md/117.md) | time,datetime | V1.0 | ⭐️⭐️ |
155+
| 118 | [默认启动主线程](md/118.md) | threading | V1.0 | ⭐️⭐️ |
156+
| 119 | [创建线程](md/119.md) | threading | V1.0 | ⭐️⭐️ |
157+
| 120 | [交替获得CPU时间片](md/120.md) | threading | V1.0 | ⭐️⭐️⭐️ |
158+
| 121 | [多线程抢夺同一个变量](md/121.md) | threading | V1.0 | ⭐️⭐️⭐️ |
159+
| 122 | [多线程变量竞争引起的问题](md/122.md) | threading | V1.0 | ⭐️⭐️⭐️ |
160+
| 123 | [多线程锁](md/123.md) | threading,lock | V1.0 | ⭐️⭐️⭐️ |
161+
| 124 | [时间转数组及常用格式](md/124.md) | time,datetime,format | V1.0 | ⭐️⭐️⭐️ |
162+
| 125 | [寻找第n次出现位置](md/125.md) | enumerator | V1.0 | ⭐️⭐️⭐️ |
163+
| 126 | [斐波那契数列前n项](md/126.md) | yield,range | V1.0 | ⭐️⭐️⭐️ |
164+
| 127 | [找出所有重复元素](md/127.md) | calendar,datetime | V1.0 | ⭐️⭐️⭐️⭐️ |
165+
| 128 | [联合统计次数](md/128.md) | Counter | V1.0 | ⭐️⭐️⭐️⭐️⭐️ |
166+
| 129 | [groupby单字段分组](md/129.md) | itertools, groupby,lambda | V1.0 | ⭐️⭐️⭐️ |
167+
| 130 | [groupby多字段分组](md/130.md) | itemgetter,itertools,groupby | V1.0 | ⭐️⭐️⭐️⭐️ |
168+
| 131 | [itemgetter和key函数](md/131.md) | operator,itemgetter,itertools | V1.0 | ⭐️⭐️⭐️⭐️⭐️ |
169+
| 132 | [sum函数计算和聚合同时做](md/132.md) | sum,generator | V1.0 | ⭐️⭐️⭐️⭐️⭐️ |
153170
更多例子正在整理发布中......

md/115.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#### 115 获取当前时间
2+
3+
```python
4+
from datetime import date, datetime
5+
from time import localtime
6+
7+
today_date = date.today()
8+
print(today_date) # 2019-12-22
9+
10+
today_time = datetime.today()
11+
print(today_time) # 2019-12-22 18:02:33.398894
12+
13+
local_time = localtime()
14+
print(strftime("%Y-%m-%d %H:%M:%S", local_time)) # 转化为定制的格式 2019-12-22 18:13:41
15+
```

md/116.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#### 116 字符时间转时间
2+
3+
```python
4+
from time import strptime
5+
6+
# parse str time to struct time
7+
struct_time = strptime('2019-12-22 10:10:08', "%Y-%m-%d %H:%M:%S")
8+
print(struct_time) # struct_time类型就是time中的一个类
9+
10+
# time.struct_time(tm_year=2019, tm_mon=12, tm_mday=22, tm_hour=10, tm_min=10, tm_sec=8, tm_wday=6, tm_yday=356, tm_isdst=-1)
11+
```

md/117.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#### 117 时间转字符时间
2+
3+
```python
4+
from time import strftime, strptime, localtime
5+
6+
In [2]: print(localtime()) #这是输入的时间
7+
Out[2]: time.struct_time(tm_year=2019, tm_mon=12, tm_mday=22, tm_hour=18, tm_min=24, tm_sec=56, tm_wday=6, tm_yday=356, tm_isdst=0)
8+
9+
print(strftime("%m-%d-%Y %H:%M:%S", localtime())) # 转化为定制的格式
10+
# 这是字符串表示的时间: 12-22-2019 18:26:21
11+
```

md/118.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#### 118 默认启动主线程
2+
3+
一般的,程序默认执行只在一个线程,这个线程称为主线程,例子演示如下:
4+
5+
导入线程相关的模块 `threading`:
6+
7+
```python
8+
import threading
9+
```
10+
11+
threading的类方法 `current_thread()`返回当前线程:
12+
13+
```python
14+
t = threading.current_thread()
15+
print(t) # <_MainThread(MainThread, started 139908235814720)>
16+
```
17+
18+
所以,验证了程序默认是在`MainThead`中执行。
19+
20+
`t.getName()`获得这个线程的名字,其他常用方法,`getName()`获得线程`id`,`isAlive()`判断线程是否存活等。
21+
22+
```python
23+
print(t.getName()) # MainThread
24+
print(t.ident) # 139908235814720
25+
print(t.isAlive()) # True
26+
```

md/119.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#### 119 创建线程
2+
3+
创建一个线程:
4+
5+
```python
6+
my_thread = threading.Thread()
7+
```
8+
9+
创建一个名称为`my_thread`的线程:
10+
11+
```python
12+
my_thread = threading.Thread(name='my_thread')
13+
```
14+
15+
创建线程的目的是告诉它帮助我们做些什么,做些什么通过参数`target`传入,参数类型为`callable`,函数就是可调用的:
16+
17+
```python
18+
def print_i(i):
19+
print('打印i:%d'%(i,))
20+
my_thread = threading.Thread(target=print_i,args=(1,))
21+
```
22+
23+
`my_thread`线程已经全副武装,但是我们得按下发射按钮,启动start(),它才开始真正起飞。
24+
25+
```python
26+
my_thread().start()
27+
```
28+
29+
打印结果如下,其中`args`指定函数`print_i`需要的参数i,类型为元祖。
30+
31+
```python
32+
打印i:1
33+
```
34+
35+
至此,多线程相关的核心知识点,已经总结完毕。但是,仅仅知道这些,还不够!光纸上谈兵,当然远远不够。

md/120.md

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
#### 120 交替获得CPU时间片
2+
3+
为了更好解释,假定计算机是单核的,尽管对于`cpython`,这个假定有些多余。
4+
5+
开辟3个线程,装到`threads`中:
6+
7+
```python
8+
import time
9+
from datetime import datetime
10+
import threading
11+
12+
13+
def print_time():
14+
for _ in range(5): # 在每个线程中打印5次
15+
time.sleep(0.1) # 模拟打印前的相关处理逻辑
16+
print('当前线程%s,打印结束时间为:%s'%(threading.current_thread().getName(),datetime.today()))
17+
18+
19+
threads = [threading.Thread(name='t%d'%(i,),target=print_time) for i in range(3)]
20+
```
21+
22+
启动3个线程:
23+
24+
```python
25+
[t.start() for t in threads]
26+
```
27+
28+
打印结果如下,`t0`,`t1`,`t2`三个线程,根据操作系统的调度算法,轮询获得CPU时间片,注意观察,`t2`线程可能被连续调度,从而获得时间片。
29+
30+
```markdown
31+
当前线程t0,打印结束时间为:2020-01-12 02:27:15.705235
32+
当前线程t1,打印结束时间为:2020-01-12 02:27:15.705402
33+
当前线程t2,打印结束时间为:2020-01-12 02:27:15.705687
34+
当前线程t0,打印结束时间为:2020-01-12 02:27:15.805767
35+
当前线程t1,打印结束时间为:2020-01-12 02:27:15.805886
36+
当前线程t2,打印结束时间为:2020-01-12 02:27:15.806044
37+
当前线程t0,打印结束时间为:2020-01-12 02:27:15.906200
38+
当前线程t2,打印结束时间为:2020-01-12 02:27:15.906320
39+
当前线程t1,打印结束时间为:2020-01-12 02:27:15.906433
40+
当前线程t0,打印结束时间为:2020-01-12 02:27:16.006581
41+
当前线程t1,打印结束时间为:2020-01-12 02:27:16.006766
42+
当前线程t2,打印结束时间为:2020-01-12 02:27:16.007006
43+
当前线程t2,打印结束时间为:2020-01-12 02:27:16.107564
44+
当前线程t0,打印结束时间为:2020-01-12 02:27:16.107290
45+
当前线程t1,打印结束时间为:2020-01-12 02:27:16.107741
46+
```

md/121.md

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
#### 121 多线程抢夺同一个变量
2+
3+
多线程编程,存在抢夺同一个变量的问题。
4+
5+
比如下面例子,创建的10个线程同时竞争全局变量`a`:
6+
7+
8+
```python
9+
import threading
10+
11+
12+
a = 0
13+
def add1():
14+
global a
15+
a += 1
16+
print('%s adds a to 1: %d'%(threading.current_thread().getName(),a))
17+
18+
threads = [threading.Thread(name='t%d'%(i,),target=add1) for i in range(10)]
19+
[t.start() for t in threads]
20+
```
21+
22+
执行结果:
23+
24+
```python
25+
t0 adds a to 1: 1
26+
t1 adds a to 1: 2
27+
t2 adds a to 1: 3
28+
t3 adds a to 1: 4
29+
t4 adds a to 1: 5
30+
t5 adds a to 1: 6
31+
t6 adds a to 1: 7
32+
t7 adds a to 1: 8
33+
t8 adds a to 1: 9
34+
t9 adds a to 1: 10
35+
```
36+
37+
结果一切正常,每个线程执行一次,把`a`的值加1,最后`a` 变为10,一切正常。
38+
39+
运行上面代码十几遍,一切也都正常。
40+
41+
所以,我们能下结论:这段代码是线程安全的吗?
42+
43+
NO!
44+
45+
多线程中,只要存在同时读取和修改一个全局变量的情况,如果不采取其他措施,就一定不是线程安全的。
46+
47+
尽管,有时,某些情况的资源竞争,暴露出问题的概率`极低极低`
48+
49+
本例中,如果线程0 在修改a后,其他某些线程还是get到的是没有修改前的值,就会暴露问题。
50+
51+
52+
53+
但是在本例中,`a = a + 1`这种修改操作,花费的时间太短了,短到我们无法想象。所以,线程间轮询执行时,都能get到最新的a值。所以,暴露问题的概率就变得微乎其微。

md/122.md

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
#### 122 多线程变量竞争引起的问题
2+
3+
只要弄明白问题暴露的原因,叫问题出现还是不困难的。
4+
5+
想象数据库的写入操作,一般需要耗费我们可以感知的时间。
6+
7+
为了模拟这个写入动作,简化期间,我们只需要延长修改变量`a`的时间,问题很容易就会还原出来。
8+
9+
```python
10+
import threading
11+
import time
12+
13+
14+
a = 0
15+
def add1():
16+
global a
17+
tmp = a + 1
18+
time.sleep(0.2) # 延时0.2秒,模拟写入所需时间
19+
a = tmp
20+
print('%s adds a to 1: %d'%(threading.current_thread().getName(),a))
21+
22+
threads = [threading.Thread(name='t%d'%(i,),target=add1) for i in range(10)]
23+
[t.start() for t in threads]
24+
```
25+
26+
重新运行代码,只需一次,问题立马完全暴露,结果如下:
27+
28+
```python
29+
t0 adds a to 1: 1
30+
t1 adds a to 1: 1
31+
t2 adds a to 1: 1
32+
t3 adds a to 1: 1
33+
t4 adds a to 1: 1
34+
t5 adds a to 1: 1
35+
t7 adds a to 1: 1
36+
t6 adds a to 1: 1
37+
t8 adds a to 1: 1
38+
t9 adds a to 1: 1
39+
```
40+
41+
看到,10个线程全部运行后,`a`的值只相当于一个线程执行的结果。
42+
43+
下面分析,为什么会出现上面的结果:
44+
45+
这是一个很有说服力的例子,因为在修改a前,有0.2秒的休眠时间,某个线程延时后,CPU立即分配计算资源给其他线程。直到分配给所有线程后,根据结果反映出,0.2秒的休眠时长还没耗尽,这样每个线程get到的a值都是0,所以才出现上面的结果。
46+
47+
48+
49+
以上最核心的三行代码:
50+
51+
```python
52+
tmp = a + 1
53+
time.sleep(0.2) # 延时0.2秒,模拟写入所需时间
54+
a = tmp
55+
```

md/123.md

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
#### 123 多线程锁
2+
3+
知道问题出现的原因后,要想修复问题,也没那么复杂。
4+
5+
通过python中提供的锁机制,某段代码只能单线程执行时,上锁,其他线程等待,直到释放锁后,其他线程再争锁,执行代码,释放锁,重复以上。
6+
7+
创建一把锁`locka`:
8+
9+
```python
10+
import threading
11+
import time
12+
13+
14+
locka = threading.Lock()
15+
```
16+
17+
通过 `locka.acquire()` 获得锁,通过`locka.release()`释放锁,它们之间的这些代码,只能单线程执行。
18+
19+
```python
20+
a = 0
21+
def add1():
22+
global a
23+
try:
24+
locka.acquire() # 获得锁
25+
tmp = a + 1
26+
time.sleep(0.2) # 延时0.2秒,模拟写入所需时间
27+
a = tmp
28+
finally:
29+
locka.release() # 释放锁
30+
print('%s adds a to 1: %d'%(threading.current_thread().getName(),a))
31+
32+
threads = [threading.Thread(name='t%d'%(i,),target=add1) for i in range(10)]
33+
[t.start() for t in threads]
34+
```
35+
36+
执行结果如下:
37+
38+
```python
39+
t0 adds a to 1: 1
40+
t1 adds a to 1: 2
41+
t2 adds a to 1: 3
42+
t3 adds a to 1: 4
43+
t4 adds a to 1: 5
44+
t5 adds a to 1: 6
45+
t6 adds a to 1: 7
46+
t7 adds a to 1: 8
47+
t8 adds a to 1: 9
48+
t9 adds a to 1: 10
49+
```
50+
51+
一起正常,其实这已经是单线程顺序执行了,就本例子而言,已经失去多线程的价值,并且还带来了因为线程创建开销,浪费时间的副作用。
52+
53+
程序中只有一把锁,通过 `try...finally`还能确保不发生死锁。但是,当程序中启用多把锁,还是很容易发生死锁。
54+
55+
注意使用场合,避免死锁,是我们在使用多线程开发时需要注意的一些问题。

0 commit comments

Comments
 (0)