“This is the 25th day of my participation in the Gwen Challenge in November. See details: The Last Gwen Challenge in 2021”

Official Python column # 46, stop! Don’t miss this zero-based article!

In the previous article, we examined the Queue. This time, we will show the problem of using queues to solve the transfer scenario.

Look again at the problem with the transfer scenario

The previous two articles have shown that money transfers repeatedly read and write amounts, resulting in errors.


xuewei_account = dict()
xuewei_account['amount'] = 100

# amount negative is the amount transferred out
def transfer(money) :
    for i in range(100000):
        xuewei_account['amount'] = xuewei_account['amount'] + money


Copy the code

Our previous posts used multiple threads to repeatedly lengthen: +1 and -1.

The logical answer should still be 100.

Here’s the whole code:

#! /usr/bin/env python
# -*- coding: utf-8 -*-
# @time: 2021/11/26 12:02 am
# @Author : LeiXueWei
# @csDN /Juejin/Wechat: Lei Xuewei
# @XueWeiTag: CodingDemo
# @File : threadsafe_queue1.py
# @Project : hello
import random
import threading
import datetime
import time

xuewei_account = dict()
xuewei_account['amount'] = 100


# amount negative is the amount transferred out
def transfer(money) :
    for i in range(100000):
        xuewei_account['amount'] = xuewei_account['amount'] + money


Create 20 tasks to transfer to student committee account repeatedly
threads = []
for i in range(10):
    t1 = threading.Thread(target=lambda: transfer(-1))
    threads.append(t1)
    t2 = threading.Thread(target=lambda: transfer(1))
    threads.append(t2)

for t in threads:
    t.start()
for t in threads:
    t.join()

print("-" * 16)
print("Active threads :", threading.active_count())
print("Active thread :", threading.current_thread().name)
print("Student Committee Account Balance:", xuewei_account)
Copy the code

Waiting for all transfer threads to finish running, we see the result is wrong:

How do you solve this problem with queues?

As mentioned earlier, multiple threads repeatedly reading and writing shared data are at the root of the problem.

Change the code to synchronous mutex mode, ensuring that one thread updates the shared data at any given time, and the problem is solved. (This is also shown before, using the Lock scheme)

How do we use queues for this?

You can think about it for about 10 seconds, and figure out how to do this based on what you’ve learned about locking and queuing.

Well, here’s the answer:

A Queue has multiple functions, one is put and one is get.

One is responsible for putting data to the end of the queue, and one is responsible for fetching elements from the end.

Suitable for transfer business, can we turn each transfer operation into an instruction/event? Like the following:

event(amount=1,acount=xuewei_account)
....
event(amount=-1,acount=xuewei_account)
....
event(amount=1,acount=xuewei_account)
....
event(amount=-1,acount=xuewei_account)
Copy the code

20 threads, each with 100,000 data reads and writes, total 2 million events.

So we can translate this into: 2 million transfer events.

Because the Queue is thread-safe, we can make 2 million concurrent transfers and hand them off to another thread.

This ensures that only one thread can read or write the xuewei_account at a time.

Transform and use queues to solve problems

Display code:

#! /usr/bin/env python
# -*- coding: utf-8 -*-
# @time: 2021/11/26 12:02 am
# @Author : LeiXueWei
# @csDN /Juejin/Wechat: Lei Xuewei
# @XueWeiTag: CodingDemo
# @File : threadsafe_queue2.py
# @Project : hello
import random
import threading
import datetime
import time
import queue

q = queue.Queue()

xuewei_account = dict()
xuewei_account['amount'] = 100


# amount negative is the amount transferred out
def transfer(money) :
    for i in range(100000):
        q.put(money)


def handle_amount() :
    while not q.empty():
        amount = q.get()
        xuewei_account['amount'] += amount


def monitor_q() :
    counter = 0
    time.sleep(3)
    while counter < 1000 and not q.empty():
        print("q size:", q.qsize())
        time.sleep(3)
        counter+=1


q_thread = threading.Thread(name="Q", target=monitor_q)
q_thread.start()
Create 20 tasks to transfer to student committee account repeatedly
threads = []
for i in range(10):
    t1 = threading.Thread(target=lambda: transfer(-1))
    threads.append(t1)
    t2 = threading.Thread(target=lambda: transfer(1))
    threads.append(t2)

for t in threads:
    t.start()

vip_thread = threading.Thread(name="Processing transfer line", target=handle_amount)
vip_thread.start()



for t in threads:
    t.join()
vip_thread.join()

print("-" * 16)
print("Active threads :", threading.active_count())
print("Active thread :", threading.current_thread().name)
print("Student Committee Account Balance:", xuewei_account)
Copy the code

Multiple threads are running to perform the transfer (sending the transfer amount to the queue).

Then run a VIP channel (single thread) to handle the transfer business of the student committee account.

It also runs a thread that monitors the queue and prints the queue’s status every once in a while.

Here is the result of the run, which is correct several times.

The final account balance is 100 after several runs. The transformation is successful.

conclusion

This session shared a thread safe Queue to solve the problem of concurrent transfers.

In fact, the code can be optimized again, in order to control the length, the code is also a lot, I hope readers can first see familiar learn, master the use of the queue.

By the way, if you like Python, please check out the Committee’s Python Basics column or Python Getting Started to Master column

Continuous learning and continuous development, I am Lei Xuewei! Programming is fun. The key is to get the technology right. Welcome to wechat, like support collection!