🇯🇵 日本語 | 🇺🇸 English | 🇪🇸 Español | 🇵🇹 Português | 🇹🇭 ไทย | 🇨🇳 中文

【Python time】บทช่วยสอน: จากการหน่วงเวลาด้วย sleep() สู่การวัดเวลาที่แม่นยำด้วย perf_counter()

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

สวัสดีครับ! ผมเป็นผู้เชี่ยวชาญที่เรียนรู้การเขียนโปรแกรมกับ AI และสร้างเว็บไซต์ 2 แห่งได้ในเดือนครึ่ง เมื่อไม่กี่เดือนก่อน ผมก็เป็นมือใหม่ที่ไม่มีความรู้ด้านการเขียนโปรแกรมเลยเหมือนกับทุกๆ คนครับ

ในระหว่างที่เรียน Python คุณเคยเห็นโค้ด `import time` บ่อยๆ ในโค้ดที่ AI สร้างขึ้นหรือในโค้ดตัวอย่างของคนอื่นไหมครับ? หลายคนอาจจะรู้สึกว่า "เจอ `time` อีกแล้ว มันทำอะไรได้กันแน่?" หรือ "แค่ใช้หยุดเวลาหรือวัดเวลาเท่านั้นเหรอ?"

ในบทความนี้ ผมจะมาอธิบายเกี่ยวกับโมดูล `time` โดยจะเล่าจากประสบการณ์ที่ผมเคยติดขัดมาก่อน เพื่อให้มือใหม่สามารถเข้าใจได้ง่ายๆ โดยเฉพาะอย่างยิ่ง เราจะเน้นไปที่วิธีการ "หยุดการทำงานชั่วคราว (sleep)" และ "การวัดเวลาที่ใช้ในการประมวลผลอย่างแม่นยำ" ซึ่งเป็นสิ่งจำเป็นอย่างยิ่งในการใช้งานจริง เช่น การสร้างเว็บไซต์หรือการเก็บข้อมูล เมื่อคุณอ่านบทความนี้จบ คุณจะสามารถใช้งานโมดูล `time` ได้อย่างคล่องแคล่วแน่นอนครับ!


1. ทำไมเราต้องจัดการกับ "เวลา"? - ความสำคัญของการรอ

คุณคิดว่าโปรแกรมยิ่งทำงานเร็วยิ่งดีใช่ไหมครับ? จริงๆ แล้ว ในบางสถานการณ์ "การตั้งใจรอ" กลับเป็นสิ่งสำคัญอย่างยิ่ง ผมจะขอยกตัวอย่าง 2 กรณีที่ผมเคยเจอตอนพัฒนาเว็บไซต์ครับ

กรณีที่ 1: การรวบรวมข้อมูลจากเว็บไซต์ (Web Scraping)

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

เทคนิค "การรอ" จึงเข้ามามีบทบาทสำคัญ การใส่คำสั่งให้รอสักสองสามวินาทีหลังจากเข้าถึงหน้าเว็บหนึ่งหน้า จะช่วยลดภาระของเซิร์ฟเวอร์ได้อย่างมาก และทำให้การรวบรวมข้อมูลเป็นไปอย่างมีมารยาท

กรณีที่ 2: การใช้งาน API

ในการใช้บริการภายนอก (API) ที่ให้ข้อมูลสภาพอากาศหรือแผนที่ การจัดการ "เวลา" ก็เป็นสิ่งที่ขาดไม่ได้ API จำนวนมากมีการจำกัดจำนวนการใช้งาน (Rate Limit) เช่น "ไม่เกิน 60 ครั้งต่อนาที" หากเราส่งคำขอเกินขีดจำกัดนี้ อาจทำให้เราไม่สามารถใช้บริการได้ชั่วคราวหรือได้รับข้อผิดพลาดกลับมา

ในกรณีนี้ก็เช่นกัน การ "รอ" เป็นเวลาที่กำหนดหลังจากส่งคำขอแต่ละครั้ง จะช่วยให้เราสามารถใช้งานได้ภายในขีดจำกัดครับ

การกำหนด "เวลาหน่วง" ให้กับโปรแกรมโดยเจตนานี้เรียกว่า Sleep Process และวิธีที่ง่ายที่สุดในการทำเช่นนี้ใน Python คือการใช้ฟังก์ชัน `time.sleep()`


2. การรออย่างแม่นยำ! หยุดการทำงานชั่วคราวด้วย `time.sleep()`

`time.sleep()` เป็นฟังก์ชันที่เรียบง่ายมาก ใช้สำหรับหยุดการทำงานของโปรแกรมชั่วคราวตามจำนวนวินาทีที่กำหนด เรามาดูวิธีใช้งานเบื้องต้นกันครับ

โค้ดด้านล่างเป็นตัวอย่างง่ายๆ ที่จะแสดงข้อความ "เริ่มการทำงาน" และหลังจากนั้น 3 วินาที จะแสดงข้อความ "ผ่านไป 3 วินาที สิ้นสุดการทำงาน" ลองคัดลอกไปรันในเครื่องของคุณดูนะครับ คุณจะเข้าใจความรู้สึกของ "การรอ" ได้ทันที


# นำเข้าโมดูล time
import time

print("เริ่มการทำงาน")

# หยุดการทำงานเป็นเวลา 3 วินาที
time.sleep(3)

print("ผ่านไป 3 วินาที สิ้นสุดการทำงาน")
        

เรายังสามารถกำหนดค่าเป็นทศนิยมได้ เช่น 0.5 (0.5 วินาที) หรือ 0.1 (100 มิลลิวินาที) ซึ่งช่วยให้ควบคุมเวลาได้ละเอียดมากยิ่งขึ้น


import time

print("รอสักครู่ 0.5 วินาที")

# หยุดการทำงานเป็นเวลา 0.5 วินาที
time.sleep(0.5)

print("เสร็จสิ้น!")
        

เมื่อใช้ร่วมกับลูป เราสามารถสร้างโปรแกรมที่ทำงานบางอย่างเป็นระยะๆ ได้อย่างง่ายดาย ตัวอย่างเช่น โปรแกรมนับถอยหลังทุกๆ 1 วินาที


import time

print("เริ่มนับถอยหลัง!")

for i in range(5, 0, -1):
    print(i)
    # รอ 1 วินาที
    time.sleep(1)

print("ปล่อยตัว!")
        

3. ฟังก์ชันนาฬิกาจับเวลา! วิธีวัดเวลาการประมวลผลอย่างแม่นยำ

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

เมื่อคุณรู้สึกว่า "การประมวลผลนี้ช้าจัง..." การวัดเวลาจริงๆ เพื่อหาจุดคอขวดเป็นสิ่งสำคัญ แทนที่จะตัดสินจากความรู้สึก

วิธีที่มือใหม่มักใช้: `time.time()`

วิธีพื้นฐานที่สุดในการวัดเวลาคือการเรียกใช้ `time.time()` ก่อนและหลังการประมวลผล แล้วนำค่ามาลบกัน `time.time()` จะคืนค่าเป็นจำนวนวินาทีที่ผ่านไปตั้งแต่ "UNIX epoch" ซึ่งเป็นเวลามาตรฐาน (1 มกราคม 1970, 00:00:00)


import time

# บันทึกเวลาก่อนเริ่มการประมวลผล
start_time = time.time()

# เขียนโค้ดที่ต้องการวัดเวลาที่นี่
# ตัวอย่างเช่น ใส่โค้ดให้รอ 1 วินาที
print("กำลังประมวลผล...")
time.sleep(1)
print("ประมวลผลเสร็จสิ้น")

# บันทึกเวลาหลังสิ้นสุดการประมวลผล
end_time = time.time()

# คำนวณและแสดงผลเวลาที่ใช้ไป
elapsed_time = end_time - start_time
print(f"เวลาที่ใช้ในการประมวลผล: {elapsed_time} วินาที")
        

【เรื่องเล่าความล้มเหลวของผม】 กับดักของ `time.time()`

จริงๆ แล้ว `time.time()` นั้นสะดวก แต่ก็มีกับดักสำคัญอยู่หนึ่งอย่าง นั่นคือ มันได้รับผลกระทบจากการเปลี่ยนแปลงเวลาของระบบคอมพิวเตอร์

ตอนที่ผมเริ่มเรียนใหม่ๆ ผมเคยใช้ `time.time()` วัดการประมวลผลที่ใช้เวลานาน แต่ระหว่างนั้น คอมพิวเตอร์ของผมได้ทำการปรับเวลาอัตโนมัติ (NTP sync) ทำให้ผลลัพธ์ที่ได้ผิดเพี้ยนไปจากเวลาจริงอย่างสิ้นเชิง ตัวอย่างเช่น หากเวลาระบบถูกปรับย้อนหลังไป 1 วินาทีระหว่างการทำงาน เวลาที่คำนวณได้ก็จะสั้นลง 1 วินาที ซึ่งทำให้การวัดผลไม่แม่นยำเลยใช่ไหมครับ?

ดังนั้นจึงมีวิธีการวัดเวลาที่น่าเชื่อถือกว่าเข้ามาแทนที่

วิธีที่มือโปรใช้: `time.perf_counter()`

`time.perf_counter()` (ย่อมาจาก performance counter) เป็นฟังก์ชันที่ออกแบบมาเพื่อการวัดค่าในระยะเวลาสั้นๆ เช่น การวัดเวลาประมวลผลโดยเฉพาะ ค่าที่ฟังก์ชันนี้คืนกลับมาไม่ใช่ "เวลา ณ ขณะนั้น" แต่เป็น เวลาที่เพิ่มขึ้นอย่างต่อเนื่องและมีความละเอียดสูงสุด ที่ระบบปฏิบัติการมีให้

คำว่า "เพิ่มขึ้นอย่างต่อเนื่อง" (monotonically increasing) คือหัวใจสำคัญ ซึ่งหมายความว่า แม้เวลาของระบบคอมพิวเตอร์จะเปลี่ยนไป ค่าของตัวนับนี้จะไม่ย้อนกลับ ดังนั้น แม้จะมีการปรับเวลาเกิดขึ้นระหว่างการประมวลผล ก็จะไม่ส่งผลกระทบต่อผลการวัด

แม้แต่ใน เอกสารอย่างเป็นทางการของ Python ก็ยังแนะนำให้ใช้ `perf_counter()` ในการวัดเวลาประมวลผล

วิธีใช้งานก็เหมือนกับ `time.time()` ทุกประการครับ


import time

# บันทึกค่าตัวนับก่อนเริ่มการประมวลผล
start_counter = time.perf_counter()

# เขียนโค้ดที่ต้องการวัดเวลาที่นี่
print("กำลังประมวลผล...")
total = 0
for i in range(10000000): # ตัวอย่างการทำงานที่ใช้เวลานาน
    total += i
print("ประมวลผลเสร็จสิ้น")

# บันทึกค่าตัวนับหลังสิ้นสุดการประมวลผล
end_counter = time.perf_counter()

# คำนวณและแสดงผลเวลาที่ใช้ไป
elapsed_time = end_counter - start_counter
print(f"เวลาที่ใช้ในการประมวลผล: {elapsed_time} วินาที")
        

หากไม่มีเหตุผลพิเศษอะไร ให้จำไว้เสมอว่าเมื่อต้องการวัดเวลาประมวลผล ให้ใช้ `time.perf_counter()` รับรองว่าไม่ผิดพลาดแน่นอนครับ!


4. 【ประยุกต์】 สำหรับนักสร้างเว็บ! ตัวจับเวลา HTML ที่ทำงานได้จริง

เราได้เรียนรู้เกี่ยวกับโมดูล `time` ของ Python กันไปแล้ว ซึ่งมีประโยชน์อย่างมากสำหรับโปรแกรมที่ทำงานฝั่งเซิร์ฟเวอร์ (backend) แต่ในฐานะนักสร้างเว็บ เราก็อยากจะแสดงผลลัพธ์เหล่านั้นให้ผู้ใช้เห็นในรูปแบบของหน้าเว็บใช่ไหมครับ?

ดังนั้น เราลองนำแนวคิดของโมดูล `time` ใน Python มาประยุกต์ใช้สร้างตัวนับถอยหลังง่ายๆ ที่ทำงานบนเบราว์เซอร์ด้วย HTML และ JavaScript กันดูครับ เช่นเดียวกับที่ `time.sleep(1)` ใน Python คือคำสั่ง "รอ 1 วินาที" ใน JavaScript เราสามารถใช้คำสั่ง `setInterval()` เพื่อ "ทำบางอย่างทุกๆ 1 วินาที" ได้เช่นกัน

โค้ดด้านล่างนี้เป็นไฟล์ HTML ทั้งหมด ลองคัดลอกโค้ดนี้ไปบันทึกเป็นไฟล์ชื่อ `timer.html` แล้วเปิดด้วยเบราว์เซอร์ดูครับ เมื่อกดปุ่ม คุณจะเห็นตัวเลขบนหน้าจอลดลงทุกๆ 1 วินาที นี่แหละครับคือขั้นตอนแรกที่จะทำให้ผู้ใช้ได้สัมผัสกับ "สิ่งที่ทำงานได้จริง"!


<!DOCTYPE html>
<html lang="th">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>ตัวนับเวลาถอยหลัง</title>
    <style>
        body {
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100vh;
            margin: 0;
            background-color: #121212;
            color: #e0e0e0;
            font-family: sans-serif;
            text-align: center;
        }
        #timer-display {
            font-size: 8rem;
            font-weight: bold;
            color: #669df6;
        }
        button {
            margin-top: 2rem;
            padding: 1rem 2rem;
            font-size: 1.5rem;
            cursor: pointer;
            border: 1px solid #5f6368;
            background-color: transparent;
            color: #8ab4f8;
            border-radius: 5px;
        }
        button:disabled {
            cursor: not-allowed;
            opacity: 0.5;
        }
    </style>
</head>
<body>
    <div>
        <h1>ตัวนับถอยหลังด้วย JavaScript</h1>
        <div id="timer-display">10</div>
        <button id="start-btn">เริ่ม</button>
        <p>ประยุกต์แนวคิดจาก time.sleep() ของ Python!</p>
    </div>

    <script>
        // เขียนโค้ด script ที่นี่
        const timerDisplay = document.getElementById('timer-display');
        const startButton = document.getElementById('start-btn');
        let timeLeft = 10;
        let timerId = null;

        function startTimer() {
            // ปิดการใช้งานปุ่มเพื่อป้องกันการคลิกซ้ำ
            startButton.disabled = true;

            // ทำงานทุกๆ 1 วินาที (1000 มิลลิวินาที)
            timerId = setInterval(() => {
                timeLeft--;
                timerDisplay.textContent = timeLeft;

                if (timeLeft <= 0) {
                    clearInterval(timerId); // หยุดตัวจับเวลา
                    timerDisplay.textContent = "Go!";
                }
            }, 1000);
        }

        // เริ่มตัวจับเวลาเมื่อมีการคลิกปุ่มเริ่ม
        startButton.addEventListener('click', startTimer);
    </script>
</body>
</html>
        

5. สรุปข้อควรระวังในการใช้โมดูล `time`

สุดท้าย เรามาทบทวนประเด็นสำคัญในการใช้งานโมดูล `time` กันอีกครั้งครับ


บทสรุป

ในครั้งนี้ เราได้เรียนรู้วิธี "หน่วงเวลา" และ "วัดเวลา" การทำงานโดยใช้โมดูล `time` ของ Python แม้จะดูเป็นโมดูลที่ไม่โดดเด่น แต่เป็นเครื่องมือที่ทรงพลังและขาดไม่ได้ในการสร้างโปรแกรมที่ใช้งานได้จริง

โดยเฉพาะอย่างยิ่ง `time.sleep()` เป็นฟังก์ชันสำคัญที่แสดงถึงการคำนึงถึงผู้อื่น (เซิร์ฟเวอร์) ส่วน `time.perf_counter()` ก็เป็นการคำนึงถึงตัวเอง (การปรับปรุงประสิทธิภาพของโค้ด)

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

ไปยังขั้นตอนต่อไป

หลังจากที่เชี่ยวชาญการจัดการเวลากับโมดูล `time` แล้ว ทำไมไม่ลองเรียนรู้เครื่องมือที่มีประโยชน์สำหรับการจัดการข้อมูลอย่างมีประสิทธิภาพดูล่ะครับ? Python ยังมีโครงสร้างข้อมูลที่ทรงพลังอีกมากมายที่มาพร้อมกับตัวติดตั้งมาตรฐาน

ในบทความถัดไป เราจะเน้นไปที่โมดูล `collections` และแนะนำฟีเจอร์ต่างๆ เช่น `defaultdict` และ `deque` ที่ทำให้ dictionary และ list สะดวกสบายยิ่งขึ้น การใช้งานสิ่งเหล่านี้ได้อย่างคล่องแคล่วจะช่วยให้คุณสามารถเขียนโค้ดที่ซับซ้อนและสวยงามยิ่งขึ้นได้ครับ!

» สรุปโครงสร้างข้อมูลที่มีประโยชน์ในโมดูล collections