Skip to content

Instantly share code, notes, and snippets.

@manwar
Last active March 1, 2025 16:32
Thread Lifecycle in Perl and Python.

-- Back to Index --


To understand the thread, one needs to fully grasp the lifecycle of thread i.e. from start to finish.

In Perl, the only threading model available is ithreads (interpreter threads), which is implemented via the threads module. In Perl, threads are ithreads, meaning each thread has its own Perl interpreter. Data is not shared by default, and you must explicitly use threads::shared to share variables. A lock is used to ensure thread-safe access to shared data.

  +-----------------+---------------------------+--------------------------------+
  | Feature         | Python                    | Perl                           |
  +-----------------+---------------------------+--------------------------------+
  | Memory Sharing  | Shared by default         | Not shared by default          |
  | Thread Model    | Lightweight (OS-managed)  | Heavyweight (Perl interpreter) |
  | Thread Safety   | Requires explicit locks   | Requires explicit locks        | 
  | Data Sharing    | Automatic (shared memory) | Explicit (via threads::shared) |
  | Performance     | Faster (less overhead)    | Slower (more overhead)         |
  +-----------------+---------------------------+--------------------------------+

In this post, I would share how we deal with it in Perl and Python.

#1: Create Thread


In Perl, we would do something like below as the main process creates a new thread.

[Source Code]

#!/usr/bin/env perl

use v5.38;
use threads;

sub worker($id) {
  say "Thread $id started.";
  sleep 5;
  say "Thread $id finished.";
}

say "Main process started.";
threads->create(\&worker, 1);
say "Main process finished.";

NOTE: Here threads->create() actually creates a new thread and immediately start executing it.

Let's see what happens when we run the code:

$ perl create-thread.pl
Main process started.
Main process finished.
Perl exited with active threads:
    1 running and unjoined
    0 finished and unjoined
    0 running and detached
Thread 1 started.
$

You see the output, it tells you the whole story.

Did you notice, the main process didn't wait for the thread to finish?

And in Python, we would do something like this:

[Source Code]

#!/usr/bin/env python3

import threading
import time

def worker(id):
    print(f"Thread {id} started.")
    time.sleep(5)
    print(f"Thread {id} finished.")

print("Main process started.")
thread = threading.Thread(target=worker, args=(1,))
thread.start()
print("Main process finished.")

Now running the above code, we get this:

$ py create-thread.py
Main process started.
Thread 1 started.
Main process finished.
Thread 1 finished.
$

Imagine, if something bad happens in the worker routine, the main process wouldn't know about it.

It just created a new thread and walked away.

#2: Join Thread


We can get around the above issue by joining the newly created thread to the calling process.

This is how you would do in Perl:

[Source Code]

#!/usr/bin/env perl

use v5.38;
use threads;

sub worker($id) {
  say "Thread $id started.";
  sleep 5;
  say "Thread $id finished.";
}

say "Main process started.";
threads->create(\&worker, 1)->join;
say "Main process finished.";

Now if we run the updated code, we get this:

$ perl join-thread.pl
Main process started.
Thread 1 started.
Thread 1 finished.
Main process finished.
$

This is much better, right?

Let's do the same in Python now:

[Source Code]

#!/usr/bin/env python3

import threading
import time

def worker(id):
    print(f"Thread {id} started.")
    time.sleep(5)
    print(f"Thread {id} finished.")

print("Main process started.")
thread = threading.Thread(target=worker, args=(1,))
thread.start()
thread.join()
print("Main process finished.")

Now running the above code, we get this:

$ py join-thread.py
Main process started.
Thread 1 started.
Thread 1 finished.
Main process finished.      
$

NOTE: In Perl, calling $thread->join returns the result back to the calling process.

Let's update the above example:.

[Source Code]

#!/usr/bin/env perl

use v5.38;
use threads;

sub worker($id) {
  say "Thread $id started.";
  sleep  5;
  say "Thread $id finished.";
  return $id * 2;
}

say "Main process started.";
my $thread = threads->create(\&worker, 1);
say "Result from thread: ", $thread->join;
say "Main process finished.";

Run the code to see the result.

$ perl result-thread.pl
Main process started.
Thread 1 started.
Thread 1 finished.
Result from thread: 2
Main process finished.
$

OK, can we do the same in Python?

Yes, you can but not that straight forward. One way is to use queue.Queue as below:

[Source Code]

#!/usr/bin/env python3

import time
import queue
import threading

def worker(id, result):
    print(f"Thread {id} started.")
    time.sleep(5)
    print(f"Thread {id} finished.")
    result.put(id * 2)

print("Main process started.")
result = queue.Queue()
thread = threading.Thread(target=worker, args=(1, result))
thread.start()
thread.join()
while not result.empty():
    print("Result from thread: ", result.get())
print("Main process finished.")

Let's check the result now:

$ py result-thread.py
Main process started.
Thread 1 started.
Thread 1 finished.
Result from thread:  2
Main process finished.
$

#3: Detach Thread


If you really want to detach the newly created thread from the main process then you can do it explicitly.

In Perl, we would do something like this:

[Source Code]

#!/usr/bin/env perl

use v5.38;
use threads;

sub worker($id) {
  say "Thread $id started.";
  sleep 5;
  say "Thread $id finished.";
}

say "Main process started.";
threads->create(\&worker, 1)->detach;
say "Main process finished.";

Run the above code and you should see this:

$ perl detach-thread.pl
Main process started.
Main process finished.
$

In Python, you create thread as daemon and it would go away as shown below:

[Source Code]

#!/usr/bin/env python3

import threading
import time

def worker(id):
    print(f"Thread {id} started.")
    time.sleep(5)
    print(f"Thread {id} finished.")

print("Main process started.")
thread = threading.Thread(target=worker, args=(1,), daemon=True)
thread.start()
print("Main process finished.")

Now running the above code, we get this:

$ py detach-thread.py
Main process started.
Thread 1 started.
Main process finished.      
$

Now the big question, when would you do this?

These are situations when:

1. You want a thread to run independently
2. You want to avoid blocking the main thread
3. You want the thread to clean up automatically
4. You don't want to wait for the thread to finish

-- Back to Index --


Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment