Created
March 6, 2017 20:13
-
-
Save dani-mp/9eceed4fa0445d522cab886f6a9db7b8 to your computer and use it in GitHub Desktop.
Producer-consumer written in Swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Note you can copy this code andn paste it in a Xcode Playground | |
// Simulates working and introduces some randomness | |
func randomSleep() { | |
sleep(arc4random_uniform(2)) | |
} | |
// Item to be produced/consumed | |
struct Item { | |
let id: Int | |
} | |
// Waits some time and produce an item | |
class Producer { | |
private var id = 0 | |
func produce() -> Item { | |
randomSleep() | |
id += 1 | |
let item = Item(id: id) | |
print("PRODUCED: \(item.id)") | |
return item | |
} | |
} | |
// Waits some time and consume an item | |
class Consumer { | |
func consume(_ item: Item) { | |
randomSleep() | |
print("consumed: \(item.id)") | |
} | |
} | |
class Program { | |
private let producer: Producer | |
private let consumer: Consumer | |
private let size: UInt | |
// Serial queue that makes sure the items are consumed in order | |
// and provides a system bound in case the size is too big | |
private let queue = DispatchQueue(label: "serial-queue") | |
private var buffer: UInt = 0 | |
init(producer: Producer, consumer: Consumer, size: UInt) { | |
self.producer = producer | |
self.consumer = consumer | |
self.size = size | |
} | |
func start() { | |
while true { | |
// The producer doesn't produce if the buffer is full | |
if buffer < size { | |
buffer += 1 | |
let item = producer.produce() | |
// When we have a produced item, we try to consume it in the | |
// serial queue that will handle it asynchronously when possible, | |
// and always in order. The producer can continue producing. | |
queue.async { [unowned self] in | |
self.consumer.consume(item) | |
self.buffer -= 1 | |
} | |
} | |
} | |
} | |
} | |
let producer = Producer() | |
let consumer = Consumer() | |
let program = Program(producer: producer, consumer: consumer, size: 5) | |
program.start() |
Yeah, I don't remember exactly, but I think that was the point.
I think @JetForMe is right. There is a risk that Program.buffer
could be modified concurrently from both queues, resulting in a garbage value or maybe a crash.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
You're accessing
Program.buffer
on both the main queue and your consumer queue.