# 120. Multithreading with Threading

Here are 10 Python code snippets showcasing multithreading using the `threading` module:

***

**1. Basic Thread Creation**

```python
import threading

def print_hello():
    print("Hello from thread!")

# Create a thread to run the function
thread = threading.Thread(target=print_hello)
thread.start()
thread.join()  # Wait for the thread to finish
```

***

**2. Running Multiple Threads**

```python
import threading

def print_number(number):
    print(f"Number: {number}")

threads = []
for i in range(5):
    thread = threading.Thread(target=print_number, args=(i,))
    threads.append(thread)
    thread.start()

# Wait for all threads to finish
for thread in threads:
    thread.join()
```

***

**3. Using Thread with Arguments**

```python
import threading

def greet(name):
    print(f"Hello, {name}!")

# Pass arguments to the thread function
thread = threading.Thread(target=greet, args=("Alice",))
thread.start()
thread.join()
```

***

**4. Shared Variable Between Threads**

```python
import threading

# Shared variable
counter = 0

def increment():
    global counter
    for _ in range(1000):
        counter += 1

# Create two threads
thread1 = threading.Thread(target=increment)
thread2 = threading.Thread(target=increment)

# Start the threads
thread1.start()
thread2.start()

# Wait for both threads to finish
thread1.join()
thread2.join()

print(f"Counter: {counter}")  # Output: Counter: 2000 (not guaranteed)
```

***

**5. Thread Synchronization with Locks**

```python
import threading

counter = 0
lock = threading.Lock()

def increment():
    global counter
    with lock:
        for _ in range(1000):
            counter += 1

# Create two threads
thread1 = threading.Thread(target=increment)
thread2 = threading.Thread(target=increment)

# Start the threads
thread1.start()
thread2.start()

# Wait for both threads to finish
thread1.join()
thread2.join()

print(f"Counter: {counter}")  # Output: Counter: 2000 (with lock)
```

***

**6. Using Threading Timer**

```python
import threading

def greet():
    print("Hello after 2 seconds!")

# Create a timer to run after 2 seconds
timer = threading.Timer(2.0, greet)
timer.start()
```

***

**7. Daemon Threads**

```python
import threading
import time

def background_task():
    while True:
        print("Background task running...")
        time.sleep(1)

# Daemon thread, will exit when main program exits
daemon_thread = threading.Thread(target=background_task)
daemon_thread.setDaemon(True)
daemon_thread.start()

# Main thread sleeps for 5 seconds
time.sleep(5)
print("Main thread ends, daemon thread is stopped automatically.")
```

***

**8. Threading with Futures (ThreadPoolExecutor)**

```python
import threading
from concurrent.futures import ThreadPoolExecutor

def print_square(number):
    print(f"Square of {number}: {number ** 2}")

# Using ThreadPoolExecutor
with ThreadPoolExecutor(max_workers=3) as executor:
    numbers = [1, 2, 3, 4, 5]
    executor.map(print_square, numbers)
```

***

**9. Multithreading for Downloading Files**

```python
import threading
import time

def download_file(file_id):
    print(f"Downloading file {file_id}...")
    time.sleep(2)
    print(f"Downloaded file {file_id}.")

# Create threads for downloading files concurrently
threads = []
for i in range(1, 4):
    thread = threading.Thread(target=download_file, args=(i,))
    threads.append(thread)
    thread.start()

# Wait for all threads to finish
for thread in threads:
    thread.join()

print("All downloads complete.")
```

***

**10. Threading with Shared List**

```python
import threading

# Shared list
numbers = []

def append_numbers():
    for i in range(5):
        numbers.append(i)

# Create threads
thread1 = threading.Thread(target=append_numbers)
thread2 = threading.Thread(target=append_numbers)

# Start threads
thread1.start()
thread2.start()

# Wait for threads to finish
thread1.join()
thread2.join()

print(f"Numbers: {numbers}")  # Output: Numbers: [0, 1, 2, 3, 4, 0, 1, 2, 3, 4]
```

***

These examples demonstrate how to create threads, share data between threads, use locks for synchronization, handle daemon threads, and more!
