官术网_书友最值得收藏!

Timeout specifications

An efficient ping test application should not be waiting for responses from its websites for a long time; it should have a set threshold for timeout that, if a server fails to return a response under that threshold, the application will deem that server non-responsive. We therefore need to implement a way to keep track of how much time has passed since a request is sent to a server. We will do this by counting down from the timeout threshold and, once that threshold is passed, all responses (whether returned or not yet returned) will be printed out.

Additionally, we will also be keeping track of how many requests are still pending and have not had their responses returned. We will be using the isAlive() method from the threading.Thread class to indirectly determine whether a response has been returned for a specific request: if, at one point, the thread processing a specific request is alive, we can conclude that that specific request is still pending.

Navigate to the Chapter05/example6.py file and consider the process_requests() function first:

# Chapter05/example6.py

import time

UPDATE_INTERVAL = 0.01

def process_requests(threads, timeout=5):
def alive_count():
alive = [1 if thread.isAlive() else 0 for thread in threads]
return sum(alive)

while alive_count() > 0 and timeout > 0:
timeout -= UPDATE_INTERVAL
time.sleep(UPDATE_INTERVAL)
for thread in threads:
print(thread.result)

The function takes in a list of threads that we have been using to make web requests in the previous examples, as well as an optional argument specifying the timeout threshold. Inside this function, we have an inner function, alive_count(), which returns the count of the threads that are still alive at the time of the function call.

In the process_requests() function, as long as there are threads that are currently alive and processing requests, we will allow the threads to continue with their execution (this is done in the while loop with the double condition). The UPDATE_INTERVAL variable, as you can see, specifies how often we check for this condition. If either condition fails (if there are no alive threads left or if the threshold timeout is passed), then we will proceed with printing out the responses (even if some might have not been returned).

Let's turn our attention to the new MyThread class:

# Chapter05/example6.py

import threading
import requests

class MyThread(threading.Thread):
def __init__(self, url):
threading.Thread.__init__(self)
self.url = url
self.result = f'{self.url}: Custom timeout'

def run(self):
res = requests.get(self.url)
self.result = f'{self.url}: {res.text}'

This class is almost identical to the one we considered in the previous example, except that the initial value for the result attribute is a message indicating a timeout. In the case that we discussed earlier where the timeout threshold specified in the process_requests() function is passed, this initial value will be used when the responses are printed out.

Finally, let's consider our main program:

# Chapter05/example6.py

urls = [
'http://httpstat.us/200',
'http://httpstat.us/200?sleep=4000',
'http://httpstat.us/200?sleep=20000',
'http://httpstat.us/400'
]

start = time.time()

threads = [MyThread(url) for url in urls]
for thread in threads:
thread.setDaemon(True)
thread.start()
process_requests(threads)

print(f'Took {time.time() - start : .2f} seconds')

print('Done.')

Here, in our URL list, we have a request that would take 4 seconds and another that would take 20 seconds, aside from the ones that would respond immediately. As the timeout threshold that we are using is 5 seconds, theoretically we should be able to see that the 4-second-delay request will successfully obtain a response, while the 20-second-delay one will not.

There is another point to be made about this program: daemon threads. In the process_requests() function, if the timeout threshold is passed while there is still at least one thread processing, then the function will proceed to print out the result attribute of each thread:

 while alive_count() > 0 and timeout > 0:
timeout -= UPDATE_INTERVAL
time.sleep(UPDATE_INTERVAL)
for thread in threads:
print(thread.result)

This means that we do not block our program until all of the threads have finished their execution by using the join() function, and the program therefore can simply move forward if the timeout threshold is reached. However, this means that the threads themselves do not terminate at this point. The 20-second-delay request, specifically, will still most likely be running after our program exits out of the process_requests() function.

If the thread processing this request is not a daemon thread (as we know, daemon threads execute in the background and never terminate), it will block the main program from finishing until the thread itself finishes. By making this thread, and any other thread, a daemon thread, we allow the main program to finish as soon as it executes the last line of its instructions, even if there are threads still running.

Let us see this program in action. Execute the code and your output should be similar to the following:

http://httpstat.us/200: 200 OK
http://httpstat.us/200?sleep=4000: 200 OK
http://httpstat.us/200?sleep=20000: Custom timeout
http://httpstat.us/400: 400 Bad Request
Took 5.70 seconds
Done.

As you can see, it took around 5 seconds for our program to finish this time. This is because it spent 5 seconds waiting for the threads that were still running and, as soon as the 5-second threshold was passed, the program printed out the results. Here we see that the result from the 20-second-delay request was simply the default value of the result attribute of the MyThread class, while the rest of the requests were able to obtain the correct response from the server (including the 4-second-delay request, since it had enough time to obtain the response).

If you would like to see the effect of non-daemon threads that we discussed earlier, simply comment out the corresponding line of code in our main program, as follows:

threads = [MyThread(url) for url in urls]
for thread in threads:
#thread.setDaemon(True)
thread.start()
process_requests(threads)

You will see that the main program will hang for around 20 seconds, as the non-daemon thread processing the 20-second-delay request is still running, before being able to finish its execution (even though the output produced will be identical).

主站蜘蛛池模板: 娄烦县| 额尔古纳市| 普兰店市| 安陆市| 宝清县| 泗洪县| 宿迁市| 宜都市| 厦门市| 屯昌县| 延边| 双鸭山市| 浪卡子县| 巴彦淖尔市| 新泰市| 白水县| 睢宁县| 阿鲁科尔沁旗| 罗山县| 惠来县| 富顺县| 阳山县| 黄浦区| 香河县| 津市市| 上犹县| 连云港市| 石家庄市| 苍溪县| 武川县| 彩票| 海南省| 竹山县| 方正县| 昌都县| 永修县| 会泽县| 芜湖县| 靖边县| 海丰县| 阜平县|