5 Python threading solutions (Barrier, Event, Condition, Lock, Semaphore) with explanation
Raise a barrier which makes both threads wait for each other before they are allowed to continue. foo prints before reaching the barrier. bar prints after reach
Raise a barrier which makes both threads wait for each other before they are allowed to continue. `foo` prints before reaching the barrier. `bar` prints after reaching the barrier. \n\n```\nfrom threading import Barrier\n\nclass FooBar:\n    def __init__(self, n):\n        self.n = n\n        self.barrier = Barrier(2)\n\n    def foo(self, printFoo):\n        for i in range(self.n):\n            printFoo()\n            self.barrier.wait()\n\n    def bar(self, printBar):\n        for i in range(self.n):\n            self.barrier.wait()\n            printBar()\n```\n\nCount the number of times foo and bar was printed and only print foo if the number of times is equal. `bar` prints if foo was printed fewer times. Use Condition and `wait_for` to syncrhonize the threads.\n\n```\nfrom threading import Condition\nclass FooBar:\n    def __init__(self, n):\n        self.n = n\n        self.foo_counter = 0\n        self.bar_counter = 0\n        self.condition = Condition()\n\n    def foo(self, printFoo):\n        for i in range(self.n):\n            with self.condition:\n                self.condition.wait_for(lambda: self.foo_counter == self.bar_counter)\n                printFoo()\n                self.foo_counter += 1\n                self.condition.notify(1)\n\n    def bar(self, printBar):\n        for i in range(self.n):\n            with self.condition:\n                self.condition.wait_for(lambda: self.foo_counter > self.bar_counter)\n                printBar()\n                self.bar_counter += 1\n                self.condition.notify(1)\n```\n\nEach thread can wait on each other to set their corresponding `foo_printed` and `bar_printed` events. Each thread also resets the corresponding printed events with `.clear()` for the next loop iteration.\n\n```\nfrom threading import Event\n\nclass FooBar:\n    def __init__(self, n):\n        self.n = n\n        self.foo_printed = Event()\n        self.bar_printed = Event()\n        self.bar_printed.set()\n\n    def foo(self, printFoo):\n        for i in range(self.n):\n            self.bar_printed.wait()\n            self.bar_printed.clear()\n            printFoo()\n            self.foo_printed.set()            \n\n    def bar(self, printBar):\n        for i in range(self.n):\n            self.foo_printed.wait()\n            self.foo_printed.clear()\n            printBar()\n            self.bar_printed.set()            \n```\n\nUse two locks for the threads to signal to each other when the other should run. `bar_lock` starts in a locked state because we always want `foo` to print first.\n\n```\nfrom threading import Lock\n\nclass FooBar:\n    def __init__(self, n):\n        self.n = n\n        self.foo_lock = Lock()\n        self.bar_lock = Lock()\n        self.bar_lock.acquire()\n\n    def foo(self, printFoo):\n        for i in range(self.n):\n            self.foo_lock.acquire()\n            printFoo()\n            self.bar_lock.release()\n\n\tdef bar(self, printBar):\n        for i in range(self.n):\n            self.bar_lock.acquire()\n            printBar()\n            self.foo_lock.release()\n```       \n\nUse two Semaphores just as we used two locks. The `foo_gate` semaphore starts with a value of 1 because we want `foo` to print first.\n\n```\nfrom threading import Semaphore\n\nclass FooBar:\n    def __init__(self, n):\n        self.n = n\n        self.foo_gate = Semaphore(1)\n        self.bar_gate = Semaphore(0)\n\n    def foo(self, printFoo):\n        for i in range(self.n):\n            self.foo_gate.acquire()\n            printFoo()\n            self.bar_gate.release()\n\n    def bar(self, printBar):\n        for i in range(self.n):\n            self.bar_gate.acquire()\n            printBar()\n            self.foo_gate.release()\n```\n\n
[]