[TH] _thread

บทความนี้เป็นเรื่องเกี่ยวกับการใช้คลาส _thread กับไมโครคอนโทรลเลอร์ ESP32 ซึ่งมีหน่วยประมวลผล 2 แกน (core) แต่อย่างไรก็ดีคลาสเธรดเป็นโมดูลที่เป็นส่วนย่อยจากโมดูล cython และขณะที่เขียนบทความยังไม่สมบูรณ์พร้อมใช้งาน โดยโครงสร้างของคลาสเป็นดังภาพที่ 1 และคลาสย่อย LockType เป็นดังภาพที่ 2

ภาพที่ 1 คลาส _thread
ภาพที่ 2 คลาส _thread.LockType

_thread

คำสั่งสำหรับสร้างเธรดมีรูปแบบดังนี้

_thread.start_new_thread( โปรแกรม, (รายการพารามิเตอร์))

การสร้างเธรดจะต้องเขียนโปรแกรมย่อยเตรียมไว้โดยกำหนดรายการพารามิเตอร์ให้ตรงกับการเรียกในคำสั่ง _thread.start_new_thread( ) และการออกจากเธรดใช้คำสั่งดังนี้

_thread.exit( )

การอ่านค่า ID ของเธรดใช้รูปแบบดังนี้

หมายเลขของเธรด = _thread.get_ident()

การกำหนดขนาดของหน่วยความจำสแตกให้กับเธรด และอ่านค่าปริมาณหน่วยความจำสแตกของเธรดใช้คำสั่งดังนี้

_thread.stack_size( ขนาดที่ต้องการ )
ขนาดสแตก = _thread.stack_size()

ตัวอย่างการใช้ _thread เป็นดังโปรแกรม demoThread1

# demoThread1
import _thread
# demoThread1
import _thread
import time
import random

runningTimes = [random.randint(3,8), random.randint(2,5)]

def myFunc(delay, id):
    global runningTimes  
    while (runningTimes[id] > 0):
        time.sleep(delay)
        print('Running thread No.{} with delay = {}.'.format(id, delay))
        runningTimes[id] -= 1
    print("Thread No.{} ... end game!".format(id))
    _thread.exit()

if __name__=="__main__":
    for i in range(2):
        print("run thread No.{}, {} times.".format(i, runningTimes[i]))
        _thread.start_new_thread(myFunc, (random.randint(1, 4), i))
    print("End of main Program")

จากตัวอย่าง demoThread1 ที่เป็นการสร้างเธรดจำนวน 2 เธรดเพื่อทำงานเป็นฉากหลัง เมื่อเธรดถูกสร้างเสร็จโปรแกรมหลักเสร็จสิ้นการทำงานแต่เธรดทั้งสองยังคงทำงานต่อไป โดยแต่ละเธรดทำงานเป็นการหน่วงเวลาตามที่สุ่มให้ และเมื่อเธรดทำงานจนครบตามจำนวนครั้งที่สุ่มจะออกจากการวนรอบ while แล้วรายงานการจบการทำงานของเธรด และเรียก _thread.exit() ดังตัวอย่างในภาพที่ 3

ภาพที่ 3 ตัวอย่างผลลัพธ์จาก demoThread1

ตัวอย่างการอ่านค่า ID ของเธรดหลักและของเธรดพร้อมแสดงจำนวนสแตกของแต่ละเธรดเป็นดัง demoThread2 และตัวอย่างผลลัพธ์ของการทำงานเป็นดังภาพที่ 4

# demoThread2
import _thread

def myFunc(id, msg):
    print("ID : {}".format(_thread.get_ident()))
    print("Stack size : {}".format(_thread.stack_size()))
    print("Thread No.{} ... end game!\n".format(id))
    _thread.exit()

if __name__=="__main__":
    print("get_ident = {}".format(_thread.get_ident()))
    for i in range(2):
        print("run thread No.{}.".format(i))
        _thread.start_new_thread(myFunc, (i, "Ho"))
    print("End of main Program\n")

ภาพที่ 4 ตัวอย่างจาก demoThread2

LockType

เป็นคลาสสำหรับสร้างสเตต (state) หรือสถานะการล็อกหรือยกเลิกการล็อกทรัพยากรที่อาจจะถูกเข้าถึงด้วยโพรเสสหลายตัวพร้อม ๆ กัน คำสั่งใช้งานของคลาสนี้ได้แก่

สถานะการถูกล็อก = Lock.locked()
Lock.acquire()
Lock.release()

ซึ่งจะพบว่ามีเพียง 3 คำสั่งสำหรับตรวจสอบสถานะ การขอล็อก และการยกเลิกการล็อกตามลำดับ ตัวอย่างโปรแกรมเป็นดังนี้

# demoThread3
import _thread

objLocked = _thread.allocate_lock()

def myFunc(id, msg):
    global objLocked
    while not objLocked.acquire():
        print("{}w/".format(id))
    if (objLocked.locked()):
        objLocked.release()
    print("Thread No.{} ... end game!\n".format(id))
    _thread.exit()

if __name__=="__main__":
    print("get_ident = {}".format(_thread.get_ident()))
    for i in range(2):
        print("run thread No.{}.".format(i))
        _thread.start_new_thread(myFunc, (i, "Ho"))
    print("End of main Program\n")

สรุป

เนื่องจากคลาส _thread ยังไม่สมบูรณ์ในขณะที่เขียนบทความ ประกอบกับ thonny ไม่รองรับการทำงานเบื้องหลังทำให้เกิดข้อผิดพลาดระหว่างทดสอบโปรแกรม อย่างไรก็ดี ทีมเราหวังว่าผู้อ่านคงได้เห็นภาพรวม และสามารถใช้การสร้างเธรดเพื่อให้มีการเรียกใช้งานโปรแกรมเบื้องหลังที่หลากหลายขึ้นนอกจากใช้การขัดจังหวะหรือการตั้งเวลา สุดท้ายขอให้สนุกกับการเขียนโปรแกรมครับ

ท่านใดต้องการพูดคุยสามารถคอมเมนท์ไว้ได้เลยครับ

แหล่งอ้างอิง

  1. _thread – multi threading support
  2. uasyncio – asynchronous I/O scheduler

(C) 2020-2021, โดย อ.ดนัย เจษฎาฐิติกุล/อ.จารุต บุศราทิจ

ปรับปรุงเมื่อ 2021-07-21, 2021-10-27