romhayh
romhayh

Reputation: 35

ThreadPoolExcutor starvation

I'm trying to run 2 different blocking functions, that utilize the same global variable a.

the code:

from concurrent.futures import ThreadPoolExecutor, as_completed
import threading
import keyboard

a = 0
def incA():
    global a
    
    while True:
        print(f'inc a: {a} -> {a+1}', end = '\n')
        a+=1
        if keyboard.is_pressed('q'):  # if key 'q' is pressed 
            break
        
def printA():
    # a is read-only
    
    while True:
        print(f'print a: {a}', end = '\n')
        if keyboard.is_pressed('q'):  # if key 'q' is pressed 
            break

with ThreadPoolExecutor(max_workers=2) as executor:
    f1 = executor.submit(incA)
    f2 = executor.submit(printA)

The desired output:

.
.
.
inc a: 15640 -> 15641
print a: 15641
inc a: 15641 -> 15642
print a: 15642
inc a: 15642 -> 15643
print a: 15643
.
.
.

the given output for this code is this:

print a: 1428inc a: 1429 -> 1430

print a: 1429
inc a: 1430 -> 1431print a: 1430

inc a: 1431 -> 1432print a: 1431

inc a: 1432 -> 1433print a: 1432

inc a: 1433 -> 1434print a: 1433

inc a: 1434 -> 1435print a: 1434

inc a: 1435 -> 1436print a: 1435

inc a: 1436 -> 1437print a: 1436

which is close to what I want, but as you can see, some of the ending of the prints (not to be confused with the printA function) gets cut off, even though I explicitly put end='\n' in the funciton.


I tried to add a Lock to the system, but it resulted in starvation. Inside the while True loop of each function, I added those lines:

def printA():
    global lock 
    # a is read-only
    
    while True:
        try:
            lock.acquire()
            print(f'print a: {a}', end = '\n')
            if keyboard.is_pressed('q'):  # if key 'q' is pressed 
                break
        finally:
            lock.release()

Which resulted in this output (starvation):

inc a: 1319 -> 1320
inc a: 1320 -> 1321
inc a: 1321 -> 1322
inc a: 1322 -> 1323
inc a: 1323 -> 1324
inc a: 1324 -> 1325
print a: 1325
print a: 1325
print a: 1325
print a: 1325
print a: 1325
print a: 1325

Upvotes: 0

Views: 107

Answers (1)

DarrylG
DarrylG

Reputation: 17176

Using the answer from Ron Serruya suggested link Print skipping newline we add the newline to the message to prevent separation.

Modified Code

from concurrent.futures import ThreadPoolExecutor, as_completed
import threading
import keyboard

a = 0
def incA():
    global a
    
    while True:
        print(f'inc a: {a} -> {a+1}\n', end = '')  # Place newline in message to prevent separation
        a+=1
        if keyboard.is_pressed('q'):  # if key 'q' is pressed 
            break
        
def printA():
    # a is read-only
    
    while True:
        print(f'print a: {a}\n', end = '') # Place newline in message to prevent separation
        if keyboard.is_pressed('q'):  # if key 'q' is pressed 
            break

with ThreadPoolExecutor(max_workers=2) as executor:
    f1 = executor.submit(incA)
    f2 = executor.submit(printA)

Output

...
inc a: 3459 -> 3460
print a: 3459
print a: 3459
inc a: 3460 -> 3461
print a: 3460
inc a: 3461 -> 3462
print a: 3461
inc a: 3462 -> 3463
inc a: 3463 -> 3464
print a: 3463
inc a: 3464 -> 3465
print a: 3464
inc a: 3465 -> 3466
print a: 3465
inc a: 3466 -> 3467
print a: 3466
...

Upvotes: 1

Related Questions