파이썬 네트워크 프로그래밍 - 스레드
스레드 개념
루틴은 프로그램이 실행되는 과정에서 프로그램 코드를 실행하는 하나의 지점으로, 실행되는 프로그램마다 적어도 하나의 루틴이 있다
단일 스레드 실행
import time
def sing():
for i in range(3):
print(" ...%d" % i)
time.sleep(1)
def dance():
for i in range(3):
print(" ...%d" % i)
time.sleep(1)
if __name__ == '__main__':
sing()
dance()
:
...0
...1
...2
...0
...1
...2
***Repl Closed***
다중 스레드 실행
다중 루틴의 실행은threading 모듈을 가져와야 합니다
매개변수 설명:
Thread([group[,target[,name[,args[,kwargs]]]]])
- group: , None
- target:
- args:
- kwargs:
- name: ,
멀티스레드 멀티태스킹
#
import time, threading
def sing():
#
print(threading.current_thread())
for i in range(3):
print(" ...%d" % i)
time.sleep(1)
def dance():
print(threading.current_thread())
for i in range(3):
print(" ...%d" % i)
time.sleep(1)
if __name__ == '__main__':
sing_thread = threading.Thread(target=sing)
dance_thread = threading.Thread(target=dance)
sing_thread.start()
dance_thread.start()
:
...0
...0
...1
...1
...2
...2
***Repl Closed***
다중 루틴은 파라미터가 있는 작업을 실행합니다
import time, threading
def sing(num):
for i in range(num):
print(" ...%d" % i)
time.sleep(1)
def dance(num):
for i in range(num):
print(" ...%d" % i)
time.sleep(1)
if __name__ == '__main__':
sing_thread = threading.Thread(target=sing, args=(3,))
dance_thread = threading.Thread(target=dance, kwargs={"num": 3})
sing_thread.start()
dance_thread.start()
:
...0
...0
...1
...1
...2
...2
***Repl Closed***
검색 스레드 목록 보기
import time, threading
def sing():
for i in range(5):
print(" ...%d" % i)
time.sleep(1)
def dance():
for i in range(5):
print(" ...%d" % i)
time.sleep(1)
if __name__ == '__main__':
#
thread_list = threading.enumerate()
print("111:", thread_list, len(thread_list))
sing_thread = threading.Thread(target=sing)
dance_thread = threading.Thread(target=dance)
thread_list = threading.enumerate()
print("222:", thread_list, len(thread_list))
#
sing_thread.start()
dance_thread.start()
# ,
thread_list = threading.enumerate()
print("333:", thread_list, len(thread_list))
:
111: [<_mainthread started="">] 1
222: [<_mainthread started="">] 1
...0
...0
333: [<_mainthread started="">, , ] 3
...1
...1
...2
...2
...3
...3
...4
...4
***Repl Closed***
주의
라인 사이의 집행은 무질서하다
import time, threading
def task():
time.sleep(1)
print(" :", threading.current_thread().name)
if __name__ == '__main__':
for _ in range(5):
sub_thread = threading.Thread(target=task)
sub_thread.start()
:
: Thread-5
: Thread-2
: Thread-3
: Thread-1
: Thread-4
***Repl Closed***
메인 라인은 모든 하위 라인이 끝난 후에 끝납니다.
#
import time, threading
#
def show_info():
for i in range(5):
print("test:", i)
time.sleep(1)
if __name__ == '__main__':
sub_thread = threading.Thread(target=show_info)
sub_thread.start()
# 5
time.sleep(10)
print("over")
:
test: 0
test: 1
test: 2
test: 3
test: 4
over
***Repl Closed***
마스터 스레드 지키기
import time, threading
def show_info():
for i in range(5):
print("test:", i)
time.sleep(1)
if __name__ == '__main__':
# ,
sub_thread = threading.Thread(target=show_info, daemon=True)
sub_thread.start()
time.sleep(10)
print("over")
:
test: 0
test: 1
test: 2
test: 3
test: 4
over
***Repl Closed***
사용자 정의 스레드
import threading
#
class MyThread(threading.Thread):
#
def __init__(self, info1, info2):
#
super().__init__()
self.info1 = info1
self.info2 = info2
#
def test1(self):
print(self.info1)
def test2(self):
print(self.info2)
# run
def run(self):
self.test1()
self.test2()
#
my_thread = MyThread(" 1", " 2")
#
my_thread.start()
:
1
2
***Repl Closed***
요약:
import time, threading
#
my_list = list()
#
def write_data():
for i in range(5):
my_list.append(i)
time.sleep(1)
print("write_data:", my_list)
#
def read_data():
print("read_data:", my_list)
if __name__ == '__main__':
#
write_thread = threading.Thread(target=write_data)
#
read_thread = threading.Thread(target=read_data)
write_thread.start()
#
write_thread.join()
print(" ...")
read_thread.start()
:
write_data: [0, 1, 2, 3, 4]
...
read_data: [0, 1, 2, 3, 4]
***Repl Closed***
다중 스레드가 동시에 전역 변수를 조작하여 데이터에 오류가 발생할 수 있습니다
import threading
#
g_num = 0
# 1
def sum_num1():
for i in range(1000000):
global g_num
g_num += 1
print("sum1:", g_num)
# 1
def sum_num2():
for i in range(1000000):
global g_num
g_num += 1
print("sum2:", g_num)
if __name__ == '__main__':
#
first_thread = threading.Thread(target=sum_num1)
second_thread = threading.Thread(target=sum_num2)
first_thread.start()
second_thread.start()
:
sum1: 1491056
sum2: 1528560
***Repl Closed***
위의 실행 결과를 통해 알 수 있듯이 다중 루틴이 전역 변수 조작 데이터에 오류가 발생했다
원인 분석: 두 라인firstthread 및 secondthread 전역 변수 gnum(기본값은 0)에서 1 연산을 진행하지만 다중 스레드가 동시에 작동하기 때문에 다음과 같은 상황이 발생할 수 있습니다.
1.gnum=0시,firstthread 획득 gnum=0.이 때 시스템은firstthread를 "sleeping"상태로 조정하고secondthread가 "running"상태로 변환, t2도 g 획득num=0
2. 그리고 secondthread에서 얻은 값을 1 더하기 및 gnum, gnum=1
3. 그리고 시스템은 다시secondthread를 "sleeping"으로 조정하고firstthread에서 "running"으로 변경합니다.루틴 t1은 이전에 얻은 0 더하기 1을 g 에 부여한다num.
4.이렇게 해서 퍼스트thread 및 secondthread 모두 gnum 더하기 1, 결과는 여전히 gnum=1.
전역 변수 데이터 오류 해결 방법
스레드 동기화: 같은 시간에 한 스레드만 전체 변수의 동기화를 조작할 수 있도록 보증한다. 바로 협동 보조로 예정된 선후 순서에 따라 운행한다.
스레드 동기화 방법:
1. 스레드 대기 (join)
2. 상호 배척 자물쇠
스레드 대기 실현 방법:
import threading
#
g_num = 0
# 1
def sum_num1():
for i in range(1000000):
global g_num
g_num += 1
print("sum1:", g_num)
# 1
def sum_num2():
for i in range(1000000):
global g_num
g_num += 1
print("sum2:", g_num)
if __name__ == '__main__':
#
first_thread = threading.Thread(target=sum_num1)
second_thread = threading.Thread(target=sum_num2)
first_thread.start()
first_thread.join()
second_thread.start()
:
sum1: 1000000
sum2: 2000000
***Repl Closed***
결론: 여러 라인이 같은 전역 변수를 동시에 조작하면 자원 경쟁 데이터 오류가 발생할 수 있다
스레드 동기화 방식은 자원 경쟁 데이터 오류 문제를 해결할 수 있지만, 이렇게 많은 임무가 단일 임무로 바뀌었다
상호 배척 자물쇠
공유 데이터에 잠금을 설정하여 같은 시간에 한 라인만 조작할 수 있도록 보증합니다
자물쇠를 빼앗은 라인은 먼저 실행하고, 자물쇠를 빼앗지 않은 라인은 기다려야 하며, 자물쇠를 다 쓴 후에 풀어야 한다. 그리고 다른 기다리는 라인은 이 자물쇠를 뺏고, 어느 라인이 뺏고, 그 라인은 다시 실행해야 한다.
구체적으로 어느 라인이 이 자물쇠를 뺏었는지 우리는 결정할 수 없다. CPU 스케줄링에 의해 결정된다
스레드 동기화는 여러 스레드가 경쟁 자원에 안전하게 접근할 수 있도록 보장할 수 있다. 가장 간단한 동기화 메커니즘은 상호 배척 자물쇠를 도입하는 것이다.
상호 배척 자물쇠가 자원에 도입된 상태: 잠금/비잠금
어떤 라인이 공유 데이터를 변경하려고 할 때 먼저 그것을 잠그고 이때 자원의 상태는'잠금'이며 다른 라인은 변경할 수 없다.이 라인이 자원을 방출할 때까지 자원의 상태를 '비잠금' 으로 바꾸어야 다른 라인이 다시 자원을 잠글 수 있습니다.상호 배척 자물쇠는 매번 한 라인만 쓰기 작업을 진행하여 다중 라인 상황에서 데이터의 정확성을 확보한다.
자물쇠를 만들려면 다음과 같이 하십시오.
a = threading.Lock()
잠기다
a.acquire()
풀어주다
a.release()
참고:
1.만약 이 자물쇠가 잠기기 전에 잠기지 않았다면acquire는 막히지 않을 것이다
2. 만약에 acquire를 호출하여 이 자물쇠에 잠기기 전에 다른 라인에 잠겼다면 이 자물쇠가 잠길 때까지 acquire는 막힐 것입니다.
# 2 100
import threading
#
g_num = 0
#
lock = threading.Lock()
# 1
def sum_num1():
#
lock.acquire()
for i in range(1000000):
global g_num
g_num += 1
print("sun1:", g_num)
#
lock.release()
# 1
def sum_num2():
#
lock.acquire()
for i in range(1000000):
global g_num
g_num += 1
print("sum2:", g_num)
#
lock.release()
if __name__ == '__main__':
#
first_thread = threading.Thread(target=sum_num1)
second_thread = threading.Thread(target=sum_num2)
#
first_thread.start()
second_thread.start()
:
sun1: 1000000
sum2: 2000000
***Repl Closed***
주의
게다가 서로 밀어내는 자물쇠를 더하면 어느 라인이 이 자물쇠를 뺏었는지 우리는 결정할 수 없다. 어느 라인이 자물쇠를 뺏었는지 어느 라인이 먼저 집행하고 뺏지 않은 라인은 기다려야 한다.
게다가 다중 임무가 순식간에 단일 임무로 바뀌면 성능이 떨어진다. 즉, 같은 시간에 한 라인만 실행할 수 있다는 것이다.
상호 배척 자물쇠를 사용하는 목적
여러 라인이 공유 데이터에 접근하여 자원 경쟁과 데이터 오류가 발생하지 않도록 보장할 수 있다
잠금, 잠금 해제 과정
하나의 라인에서 자물쇠를 호출하는 acquire () 방법으로 자물쇠를 얻었을 때, 자물쇠는'locked '상태로 들어갑니다.
매번 하나의 라인만 자물쇠를 얻을 수 있습니다. 만약 다른 라인이 이 자물쇠를 얻으려고 시도한다면, 이 라인은 'Blocked' 상태로 바뀌어 '막힘' 이라고 합니다. 자물쇠가 있는 라인이 자물쇠를 호출하는release () 방법으로 자물쇠를 풀면 자물쇠는 'unlocked' 상태로 들어갑니다.
스레드 스케줄러는 동기화 차단 상태의 스레드 중 하나를 선택하여 자물쇠를 가져오고 이 스레드를'running'상태로 진입시킵니다
고정 자물쇠
상대방이 자물쇠를 풀기를 기다리는 장면이 바로 자물쇠입니다.
아래 표시에 따라 목록에서 값을 얻지만, 같은 시간에 한 라인만 값을 얻을 수 있음을 보증합니다
# :
import time, threading
#
lock = threading.Lock()
def get_value(index):
#
lock.acquire()
print(threading.current_thread().name)
my_list = [3, 6, 8, 1]
#
if index >= len(my_list):
print(" :", index)
return
value = my_list[index]
print(value)
time.sleep(1)
#
lock.release()
if __name__ == '__main__':
#
for i in range(30):
sub_thread = threading.Thread(target=get_value, args=(i,))
sub_thread.start()
잠금 해제:
# :
import time, threading
#
lock = threading.Lock()
def get_value(index):
#
lock.acquire()
print(threading.current_thread().name)
my_list = [3, 6, 8, 1]
#
if index >= len(my_list):
print(" :", index)
lock.release()
return
value = my_list[index]
print(value)
time.sleep(1)
#
lock.release()
if __name__ == '__main__':
#
for i in range(30):
sub_thread = threading.Thread(target=get_value, args=(i,))
sub_thread.start()
소결: 서로 밀어내는 자물쇠를 사용할 때 자물쇠가 사라지는 문제에 주의해야 하며, 적당한 곳에서 자물쇠를 풀어주는 것에 주의해야 한다.
사라진 자물쇠가 발생하면 응용의 정지 응답을 초래할 수 있습니다
개인 독립 블로그: www.limiao.Tech Board
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
다양한 언어의 JSONJSON은 Javascript 표기법을 사용하여 데이터 구조를 레이아웃하는 데이터 형식입니다. 그러나 Javascript가 코드에서 이러한 구조를 나타낼 수 있는 유일한 언어는 아닙니다. 저는 일반적으로 '객체'{}...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.