# Fixed Job Queue Implementation

The race condition in the original code occurs because multiple threads can access and modify the shared `jobs` and `results` dictionaries simultaneously without synchronization. Here's the corrected implementation with proper locking:

```python
import threading

class JobQueue:
    def __init__(self):
        self.jobs = {}
        self.results = {}
        self.lock = threading.Lock()

    def add_job(self, job_id, data):
        with self.lock:
            self.jobs[job_id] = data
            return job_id

    def process_job(self, job_id, processor):
        with self.lock:
            # Atomic check and removal of job
            if job_id not in self.jobs:
                return False

            data = self.jobs[job_id]
            del self.jobs[job_id]

        # Execute processor outside the lock to avoid blocking other operations
        result = processor(data)

        with self.lock:
            self.results[job_id] = result

        return True

    def get_result(self, job_id):
        with self.lock:
            return self.results.get(job_id)
```

## Key Fixes

1. **Added a `threading.Lock`** - Protects all access to shared data structures.

2. **Protected `add_job`** - Ensures only one thread can add jobs at a time.

3. **Atomic job processing** - The check `if job_id not in self.jobs` and the removal `del self.jobs[job_id]` are now within the same lock, preventing other threads from stealing the job between check and execution.

4. **Protected `get_result`** - Thread-safe access to results.

5. **Optimized lock scope** - The processor function runs outside the lock to avoid blocking other operations while the job is being processed.

## Additional Improvements (Optional)

For production use, you may also want to:

```python
import threading
from typing import Any, Callable, Optional, Dict

class JobQueue:
    def __init__(self):
        self.jobs: Dict[str, Any] = {}
        self.results: Dict[str, Any] = {}
        self.lock = threading.Lock()

    def add_job(self, job_id: str, data: Any) -> str:
        with self.lock:
            self.jobs[job_id] = data
            return job_id

    def process_job(self, job_id: str, processor: Callable) -> bool:
        with self.lock:
            if job_id not in self.jobs:
                return False
            data = self.jobs[job_id]
            del self.jobs[job_id]

        try:
            result = processor(data)
        except Exception:
            with self.lock:
                self.results[job_id] = None
            return False

        with self.lock:
            self.results[job_id] = result

        return True

    def get_result(self, job_id: str) -> Optional[Any]:
        with self.lock:
            return self.results.get(job_id)

    def clear(self):
        with self.lock:
            self.jobs.clear()
            self.results.clear()

    def get_job_count(self) -> int:
        with self.lock:
            return len(self.jobs)
```

This version adds type hints, exception handling, and utility methods for a more robust and production-ready implementation.