python3.13无GIL自由线程新特性

安装Python3.13.2

Windows: 安装python3.13, 直接下载即可,在安装目录的 python3.13t

  • 在”Advanced Options”下,确保选择“Download free-threaded binaries(experimental)”选项,然后点击“安装”

Ubuntu: 下载代码编译

1
2
3
4
5
6
7
8
./configure --disable-gil --enable-optimizations
make

# 会覆盖系统的python
make install

# 或者, 不覆盖系统的python
make altinstall

运行测试代码:

1
2
3
4
5
6
7
8
9
import threading


def f():
while 1:
pass

for i in range(16):
threading.Thread(target=f).start()

使用python3.13tpython3.13 运行代码 python3.13t test.pyPYTHON_GIL=0 python3.13 test.pypython3.13 -Xgil=0 test.py, 查看16核CPU的占用率100%

  • 如何没有设置 PYTHON_GIL, 那么 python3.13默认是无GIL的

  • 对比python3.13的无GIL版本:

  • 对比python3.13 的有GIL版本:

  • 对比python3.12版本(有GIL)

PEP-703

https://peps.python.org/pep-0703/

关于容器的线程安全

https://peps.python.org/pep-0703/#container-thread-safety

python底层的实现中,每个object内部使用临界区来实现线程同步

Per-object locks with critical sections provide weaker protections than the GIL. Because the GIL doesn’t necessarily ensure that concurrent operations are atomic or correct, the per-object locking scheme also cannot ensure that concurrent operations are atomic or correct. Instead, per-object locking aims for similar protections as the GIL, but with mutual exclusion limited to individual objects.

  • 在object加锁
    • list.append, list.insert, list.repeat, PyList_SetItem
    • dict.__setitem__, PyDict_SetItem
    • list.clear, dict.clear
    • list.__repr__, dict.__repr__, etc.
    • list.extend(iterable)
    • setiter_iternext
  • 在2个object加锁
    • list.extend(list), list.extend(set), list.extend (dictitems), and other specializations where the implementation is specialized for argument type.
    • list.concat(list)
    • list.__eq__(list), dict.__eq__(dict)
  • 无锁
    • len(list) i.e., list_length(PyListObject *a)
    • len(dict)
    • len(set)
  • 看情况, 需要根据内存分配器的实现而定, 尽量会少用锁提升性能
    • list[idx] (list_subscript)
    • dict[key] (dict_subscript)
    • listiter_next, dictiter_iternextkey/value/item
    • list.contains

no-GIL + async/await

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
48
49
50
51
52
53
54
55
56
57
58
import threading
import asyncio
import time

async def async_range(n):
for i in range(n):
yield i

# 异步任务
async def async_task(name):
for i in range(20):
print(f"异步任务 {name} 开始执行")
async for i in async_range(1000_0000):
pass
print(f"异步任务 {name} 执行结束")
return f"结果 {name}"

# 在线程中运行异步任务
def run_async_in_thread(name):
# 创建一个新的事件循环
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)

# 运行异步任务
result = loop.run_until_complete(async_task(name))

# 关闭事件循环
loop.close()

return result

# 线程函数
def thread_function(name):
print(f"线程 {name} 开始执行")
result = run_async_in_thread(f"Async-{name}")
print(f"线程 {name} 执行结束,结果: {result}")

# 主函数
def main():

start = time.time()
# 创建并启动线程
threads = []
for i in range(16):
thread = threading.Thread(target=thread_function, args=(f"Thread-{i+1}",))
threads.append(thread)
thread.start()

# 等待所有线程完成
for thread in threads:
thread.join()


print(f"所有任务执行完毕, 耗时: {time.time() - start} s" )


if __name__ == "__main__":
main()
  • 多进程
    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
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    import threading
    import asyncio
    import time
    import multiprocessing
    from typing import List


    async def async_range(n):
    for i in range(n):
    yield i


    # 异步任务
    async def async_task(name):
    for i in range(20):
    print(f"异步任务 {name} 开始执行")
    async for i in async_range(1000_0000):
    pass
    print(f"异步任务 {name} 执行结束")
    return f"结果 {name}"


    # 在进程中运行异步任务
    def run_async_in_thread(name):
    # 创建一个新的事件循环
    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)

    # 运行异步任务
    result = loop.run_until_complete(async_task(name))

    # 关闭事件循环
    loop.close()

    return result


    # 线程函数
    def thread_function(name):
    print(f"进程 {name} 开始执行")
    result = run_async_in_thread(f"Async-{name}")
    print(f"进程 {name} 执行结束,结果: {result}")


    # 主函数
    def main():

    start = time.time()
    # 创建并启动进程
    processes: List[multiprocessing.Process] = []
    for i in range(16):
    process = multiprocessing.Process(
    target=thread_function, args=(f"Process-{i+1}",)
    )
    processes.append(process)
    process.start()

    # 等待所有进程完成
    for process in processes:
    process.join()

    print(f"所有任务执行完毕, 耗时: {time.time() - start} s")


    if __name__ == "__main__":
    main()

运行时间对比

  • 使用 No-GIL python3.13 耗时66s
  • 使用 GIL python3.13 耗时375s
  • 使用带GIL的多进程65s

有了multiprocessing,为什么还要No-GIL ?

因为multiprocessing是多进程, 每个进程都有自己的内存空间, 进程间通信成本较高

  • 进程间通信手段: Queue, Pipe, Semaphore, Socket, File
  • 线程间同步: Lock, RLock, Condition, Event, Queue, Barrier

NO-GIL的最大优势:

  • 同一程序空间,线程间通信简单

[附] 多进程和多线程的区别:

  • 多进程: 每个进程有自己的内存空间

    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
    from multiprocessing import Process

    # 定义一个全局变量
    counter = 0

    def increment():
    global counter
    for _ in range(1000000):
    counter += 1

    if __name__ == "__main__":
    # 创建两个进程
    p1 = Process(target=increment)
    p2 = Process(target=increment)

    # 启动进程
    p1.start()
    p2.start()

    # 等待进程完成
    p1.join()
    p2.join()

    # 打印最终的全局变量值
    print(f"Final counter value: {counter}")

    运行结果:

    1
    2
    3
    4
    $ python3.13 test_process.py
    counter: 1000000
    counter: 1000000
    Final counter value: 0

    因为主进程中的 counter没有改过, 所以最终的结果是0

  • 多线程: 共享内存空间

    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
    # from multiprocessing import Process
    from threading import Thread

    # 定义一个全局变量
    counter = 0

    def increment():
    global counter
    for _ in range(1000000):
    counter += 1

    if __name__ == "__main__":
    # 创建两个进程
    p1 = Thread(target=increment)
    p2 = Thread(target=increment)

    # 启动进程
    p1.start()
    p2.start()

    # 等待进程完成
    p1.join()
    p2.join()

    # 打印最终的全局变量值
    print(f"Final counter value: {counter}")
    • 使用 GIL版本运行结果:

      1
      2
      $ python3.13 -Xgil=1 test_thread.py
      Final counter value: 2000000
    • 使用 No-GIL版本运行结果:

      1
      2
      3
      4
      5
      6
      7
      8
      $ python3.13 -Xgil=0 test_thread.py
      Final counter value: 1139283
      $ python3.13 -Xgil=0 test_thread.py
      Final counter value: 1170427
      $ python3.13 -Xgil=0 test_thread.py
      Final counter value: 1415999
      $ python3.13 -Xgil=0 test_thread.py
      Final counter value: 1205918
      • 因为没有GIL的限制, 且没有加锁, 所以多线程的结果是不确定的
    • 对counter加锁

    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
    from threading import Thread, Lock

    # 定义一个全局变量
    counter = 0

    lock = Lock()

    def increment():
    global counter
    for _ in range(1000000):
    lock.acquire() # 获取锁
    counter += 1
    lock.release() # 释放锁

    if __name__ == "__main__":
    # 创建两个进程
    p1 = Thread(target=increment)
    p2 = Thread(target=increment)

    # 启动进程
    p1.start()
    p2.start()

    # 等待进程完成
    p1.join()
    p2.join()

    # 打印最终的全局变量值
    print(f"Final counter value: {counter}")

    使用无GIL python运行结果:

    1
    2
    $ python3.13 -Xgil=0 test_thread.py
    Final counter value: 2000000

Raydium做市资金量计算

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
from math import sqrt


def calc_market(market_cap_in_usd: float, sol_price_in_usd: float):

# k = x * y 量纲 t*s , s 为 sol , t 为 token
k = 79 * (10_0000_0000 - 7_9310_0000)

P = 0 # 量纲 s/t
if True:
# 量纲 u/s , u为usd
target_sol_price = sol_price_in_usd

# 量纲 u/t , u为usd
target_token_price = market_cap_in_usd / 10_0000_0000

# p = (u/t) / (u/s) , 目标量纲为 s/t
P = target_token_price / target_sol_price
# print(p)

# y^2 = k / p , 量纲为 ts / (s/t) = t^2 , 因此 y的量纲为 t
Y = sqrt(k / P)

# x = p * y , 量纲为 (s/t) * t = s , 因此 x 的量纲为 s
X = P * Y

return X, Y


target_market_cap_in_usd_lists = [1_0000_0000 * i for i in range(1, 11)]
sol_price_in_usd = 230

for target_market_cap in target_market_cap_in_usd_lists:
ret = calc_market(target_market_cap, sol_price_in_usd)
print(
f"拉盘到{target_market_cap:0.0f}美金市值, 所需资金数量: {ret[0]:0.0f} 枚SOL , 约合 {ret[0] * sol_price_in_usd : 0.0f} USDT"
)

计算结果:

1
2
3
4
5
6
7
8
9
10
拉盘到100000000美金市值,  所需资金数量: 2666 枚SOL , 约合  613137 USDT
拉盘到200000000美金市值, 所需资金数量: 3770 枚SOL , 约合 867107 USDT
拉盘到300000000美金市值, 所需资金数量: 4617 枚SOL , 约合 1061985 USDT
拉盘到400000000美金市值, 所需资金数量: 5332 枚SOL , 约合 1226275 USDT
拉盘到500000000美金市值, 所需资金数量: 5961 枚SOL , 约合 1371017 USDT
拉盘到600000000美金市值, 所需资金数量: 6530 枚SOL , 约合 1501873 USDT
拉盘到700000000美金市值, 所需资金数量: 7053 枚SOL , 约合 1622209 USDT
拉盘到800000000美金市值, 所需资金数量: 7540 枚SOL , 约合 1734214 USDT
拉盘到900000000美金市值, 所需资金数量: 7997 枚SOL , 约合 1839412 USDT
拉盘到1000000000美金市值, 所需资金数量: 8430 枚SOL , 约合 1938910 USDT

我的奶奶

奶奶的生卒年

奶奶生于民国十九年农历六月初三(公元1930年6月28日), 卒于公元2024年11月1日(农历十月初一), 享年95岁

爷爷生于民国八年(公元1920年),卒于1991年,享年72岁

奶奶的出生地

谭家岭

奶奶父母和兄弟姐妹

  • 据奶奶回忆,她的父母早亡(具体年份未知,推测在1943年前), 留下奶奶和 妹妹(老二)和弟弟(老三) 三人
  • 奶奶的妹妹应该是1932年生人,比奶奶小两岁
  • 据奶奶回忆,她的弟弟是饿死的(推测是营养不良和疾病导致), 推测年份1943年前,因此,弟弟死亡时最大年龄不超过10岁
  • 据奶奶回忆,13岁嫁给爷爷(即1943年)

奶奶的亲妹妹

奶奶儿孙

  • 据奶奶回忆,
  • 大女
  • 二女
  • 三儿
  • 四儿:
  • 五儿: 1968
  • 六儿:

  • 奶奶80岁大寿那天(2010年),我得到重点高中通知书
  • 奶奶90岁大寿那天(2020年),我弟弟高考成绩出来,创造高中最好成绩,双喜临门
  • Copyrights © 2021-2025 youngqqcn

请我喝杯咖啡吧~