-1

I've always struggled with multi-threaded program. My current understanding is when i create the multiple threads, it will run parallel. But it is not happening.

Here is code Ihave came so far.

#!/usr/bin/python

import threading
import time

class myThread (threading.Thread):
    def __init__(self, threadID, name, counter):
        threading.Thread.__init__(self)
        self.threadID = threadID
        self.name = name
        self.counter = counter
    def run(self):
        print "Starting " + self.name
        # Get lock to synchronize threads
        threadLock.acquire()
        print_time(self.name, self.counter, 3)
        # Free lock to release next thread
        threadLock.release()
        print "Exiting " + self.name

def print_time(threadName, delay, counter):
    while counter:
        time.sleep(delay)
        print "%s: %s" % (threadName, time.ctime(time.time()))
        counter -= 1

threadLock = threading.Lock()
threads = []

# Create new threads
thread1 = myThread(1, "Thread-1", 1)
thread2 = myThread(2, "Thread-2", 2)

# Start new Threads
thread1.start()
thread2.start()

# Add threads to thread list
threads.append(thread1)
threads.append(thread2)

# Wait for all threads to complete
for t in threads:
    t.join()
print "Exiting Main Thread"

Output:

Starting Thread-1
Starting Thread-2
Thread-1: Mon Nov 16 23:49:33 2015
Thread-1: Mon Nov 16 23:49:34 2015
Thread-1: Mon Nov 16 23:49:35 2015
Exiting Thread-1
Thread-2: Mon Nov 16 23:49:37 2015
Thread-2: Mon Nov 16 23:49:39 2015
Thread-2: Mon Nov 16 23:49:41 2015
Exiting Thread-2
Exiting Main Thread

I except both thread should run in parallel. but it is not happening. Please help me understanding the multithread program, Since i am new to multi-thread program in python. Thanks in advance

Sandy
  • 233
  • 1
  • 2
  • 18

2 Answers2

3

You acquired a shared lock before printing in each thread. So of course they're not interleaving prints, the lock isn't being released while the first thread to acquire it sleeps, so the second thread is stuck waiting for the lock the whole time.

If the goal was to protect only the time prints, not the loop of prints from start to finish, you'd change the code for run and print_time to:

    def run(self):
        print "Starting " + self.name
        # No locking now, letting print_time do it
        print_time(self.name, self.counter, 3)
        print "Exiting " + self.name

def print_time(threadName, delay, counter):
    while counter:
        time.sleep(delay)
        # Use a with statement to avoid problems with lock being held forever
        # if release() call skipped due to an exception (or coding error)
        with threadLock: # Hold lock during time and print, and at no other time
            print "%s: %s" % (threadName, time.ctime(time.time()))
        counter -= 1
ShadowRanger
  • 143,180
  • 12
  • 188
  • 271
  • @ShawdowRanger, Thanks for the reply. to make it run in parallel, what would be the best way? – Sandy Nov 16 '15 at 18:31
  • @Sandy: Was already adding that. Note: CPython's GIL means that, given the lack of access to shared data structures, you probably don't benefit from this lock. Locking to protect output from interleaving _might_ be beneficial (and good practice), but you'd want to add the locking (individually) around the `Starting` and `Ending` prints too in that case. – ShadowRanger Nov 16 '15 at 18:34
  • @Sandy: As an additional note, read up on the [CPython GIL](https://wiki.python.org/moin/GlobalInterpreterLock). In general, threading doesn't get you a big performance boost in Python (at least, not in CPU bound cases; for I/O bound code it can provide a benefit). – ShadowRanger Nov 16 '15 at 18:36
  • After adding your code Output is only Starting Thread-1 Starting Thread-2 . I really don't know what causing this issue – Sandy Nov 16 '15 at 18:39
  • @Sandy: Psychic debugging: You added the code to `print_time`, but didn't remove the `acquire` and `release` from `run`. Thus, one thread acquires the lock in `run`, then tries to reacquire it in `print_time`, and `Lock` is not reentrant; the lock can't be `acquire`-ed twice, even within the same thread. – ShadowRanger Nov 16 '15 at 18:42
1

By the way:

CPython implementation detail: In CPython, due to the Global Interpreter Lock, only one thread can execute Python code at once (even though certain performance-oriented libraries might overcome this limitation). If you want your application to make better use of the computational resources of multi-core machines, you are advised to use multiprocessing or concurrent.futures.ProcessPoolExecutor. However, threading is still an appropriate model if you want to run multiple I/O-bound tasks simultaneously.

Python threading

With or without the locks, you can't get true parallelism. You have to use multiprocessing for that.

Adam Acosta
  • 603
  • 3
  • 6
  • Yar, I commented to that effect; that said, the OP's particular case (at least as written) is I/O, lock acquisition and `sleep` bound, and all of those actions (being blocking actions implemented at the C layer) release the GIL. – ShadowRanger Nov 16 '15 at 18:40