파이썬 쓰레드, 동기화, 파일IO
- 프로그래밍/Python
- 2022. 4. 19.
파이썬에서 쓰레드를 작성하는 방법
threading모듈을 이용해서 작업
싱글쓰레드는 모든 코드의 실행이 끝나야 주 쓰레드가 종료된다. 주 쓰레드가 종료되어야 프로세스가 종료된다.
멀티쓰레드는 주 쓰레드가 먼저 종료될 수 있다. 실행 중인 쓰레드가 하나라도 있으면 프로세스는 종료되지 않는다.
<<<Thread클래스를 생성해서 사용하는 방법>>>
1. 멀티쓰레드로 즉, 독립적인 실행흐름을 갖고 실행하고 싶은 코드를 함수를 정의해서 구현한다.
2. threading.Thread()객체를 생성할때 target이라는 매개변수에 멀티쓰레드로 실행하고 싶은 함수명(1번에서 정의한 함수명)을 지정
객체변수 = threading.Thread(target="함수명", args=(a,b....))
threading.Thread클래스의 매개변수
target : 쓰레드로 실행할 함수명을 명시
args : target에서 명시한 함수가 매개변수를 필요로 하는 경우 매개변수를 튜플로 만들어서 작업
3. 객체의 start메소드를 호출해서 쓰레드를 start시킨다.
멀티쓰레드를 실행하기 위해서 start를 호출하면 스케쥴러가 적절한 시점에 쓰레드를 만들고 target 속성에 지정한 함수를 실행한다
import time
import threading
class MyThread:
def __init__(self, name):
self.name = name
def threadRun(self):
for i in range(10):
print(str(i)+":"+self.name)
time.sleep(0.5)
if __name__ == '__main__':
print("주 쓰레드(main쓰레드) 시작")
t1 = MyThread("t1")
t2 = MyThread("t2")
mythread1 = threading.Thread(target=t1.threadRun)
mythread2 = threading.Thread(target=t2.threadRun)
#쓰레드 실행
mythread1.start()
mythread2.start()
print("주 쓰레드(main쓰레드) 종료")
<<Thread클래스를 상속받아 사용하는 방법>>
Thread클래스의 하위 클래스를 만들고 작업
멀티쓰레드 프로그래밍을 하기 위해 별도의 클래스를 작성하고 작업한다.
1. threading.Thread클래스를 상속해서 클래스 정의
class 클래스명(threading.Thread)
2. 생성자의 첫 번째 문장에서 Thread클래스의 생성자를 호출해야 한다.
def __init__(self):
threading.Thread.__init__(self)
3. threading.Thread클래스의 run메소드를 재정의 한다.
-> 독립적으로 실행하고 싶은 코드를 run에 구현한다. (threading,Thread객체를 생성하면서 target에 명시했던 함수 안에구현했던 내용을 run메소드 안에 구현하기)
def run(self):
#쓰레드로 실행할 코드 정의
4. start()를 호출한다.
start가 호출되면 쓰레드를 만들어서 실행할 준비를 하고 적당한 시점에 쓰레드를 생성해서 그 쓰레드 객체의 run메소드를 실행한다
target속성에 정의한 함수를 실행
from threading import Thread
import requests
import time
class gethtml(Thread):
def __init__(self, url):
Thread.__init__(self)
self.url = url
def run(self):
response = requests.get(self.url)
time.sleep(1)
print(self.url,len(response.text),response.text)
print("test....")
t1 = gethtml("https://www.naver.com")
t1.start()
time.sleep(1)
print("프로그램종료++++++++++++++++++")
<<< 동기화 >>>
멀티 쓰레드 프로그래밍을 할 때 쓰레드들이 공유자원에 동시에 접근하는 경우 무결하지 못한 데이터가 발생될 수 있다.
공유자원 - 모든 쓰레드가 사용하는 객체
무결하지 못한 데이터가 발생 - 데이터를 신뢰할 수 없게 된다.
임계영억(Critical section)
공유 데이터 접근해서 사용하는 코드
1.문제해결을 위한 방법
1) 상호배제 - 두 개 이상의 쓰레드가 임계영역에 진입하지 못하도록 하는 것
Lock객체를 이용해서 상호 배제를 처리
한 쓰레드가 공유 객체를 점유해서 사용하는 동안 공유객체에 lock걸어서 접근하지 못하도록 처리
2. 적용 방법
1) 공유객체 내부에서 Lock객체를 생성
2) 공유해서 사용하는 부분의 전에 Lock객체의 acquire()를 호출하여 lock을 건다.
즉, 한 스레드만 공유 객체를 사용하고 다른 쓰레드는 대기 상태에 있을 수 있도록 작업
3) 소유해서 사용하던 락을 해제 release()
- 대기 상태에 있는 쓰레드가 공유객체를 점유하고 lock객체를 획득
######### User.py #######
from threading import Thread
class User(Thread):
def __init__(self, name, toiletObj): #toilet객체는 사용자 쓰레드가 공유해서 사용할 객체
super().__init__()
self.name = name
self.toiletObj = toiletObj
def run(self): # 쓰레드로 실행되어지는 run 메소드에서 공유객체를 사용
self.toiletObj.open(self.name)
######### toliet.py ###########
from threading import Lock
class Toilet:
def __init__(self):
self.lock = Lock()
#공유해서 사용할 기능의 정의된 메소드
def open(self,name):
#Lock객체의 auquire메소드를 호출하면 Lock객체를 사용하는 쓰레드가 갖는다.
# 쓰레드가 점유해서 Toilet객체의 open메소드를 사용하는 동안 다른 쓰레드가
# 호출하지 못하고 대기 상태에 있을 수 있도록 설정
self.lock.acquire()
print(name,"이 문열고 들어옴")
for i in range(10000000):
if i == 10000:
print(name,"이 끙~~~~~~~아~~~~~~")
print(name,"이 문열고 나감")
self.lock.release() #한 쓰레드에서 작업이 끝나면 lock을 해제
####### toliet_test.py ############
from toilet import Toilet
from User import User
print("작업시작")
# 공유객체 만들기
toilet = Toilet()
#쓰레드 객체를 7개 생성해서 작업해보기
User("RM",toilet).start()
User("제이홉",toilet).start()
User("슈가",toilet).start()
User("진",toilet).start()
User("뷔",toilet).start()
User("정국",toilet).start()
User("지민",toilet).start()
파일 IO에 대해서 알아보자.
#파일 입출력
#파일 입출력을 위해서 파일을 먼저 생성 - 내장함수 open(파일에 대한 설정)
#r = read, w = write, a = append(파일 마지막 내용에 추가)
f = open("./test.txt","w")
f.write("test raspberry pi")
f.close()
f = open("test.txt", "r")
data = f.read() # 만약에 네트워크로 전송을 한다면 데이터를 바이너리 데이터로 변환
#바이너리 데이터 : 바이트배열
print(data)
f.close()