Unbiased select

One of the most useful ways to work with channels is the select {} clause we saw in Chapter 8Threads and Coroutinesin the Producers section. 

But select is inherently biased. If two events happen at the same time, it will select the first clause.

In the following example, we'll have a producer that sends five values with a very short delay:

fun producer(name: String, repeats: Int) = produce {
repeat(repeats) {
delay(1)
send(name)
}
}

We'll create three such producers and see the results:

val repeats = 10_000
val p1 = producer("A", repeats)
val p2 = producer("B", repeats)
val p3 = producer("C", repeats)

val results = ConcurrentHashMap<String, Int>()
repeat(repeats) {
val result = select<String> {
p1.onReceive { it }
p2.onReceive { it }
p3.onReceive { it }
}

results.compute(result) { k, v ->
if (v == null) {
1
}
else {
v + 1
}
}
}

println(results)

We run this code five times. Here are some of the results:

{A=8235, B=1620, C=145}
{A=7850, B=2062, C=88}
{A=7878, B=2002, C=120}
{A=8260, B=1648, C=92}
{A=7927, B=2011, C=62}

As you can see, A almost always wins, while C is always third. The more repeats you set, the larger the bias gets.

Now let's use selectUnbiased instead:

...
val
result = selectUnbiased<String> {
p1.onReceive { it }
p2.onReceive { it }
p3.onReceive { it }
}
...

The results of the first five executions may look like this:

{A=3336, B=3327, C=3337}
{A=3330, B=3332, C=3338}
{A=3334, B=3333, C=3333}
{A=3334, B=3336, C=3330}
{A=3332, B=3335, C=3333}

Not only are the numbers distributed more evenly now, but all clauses have an equal chance of being selected.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.147.67.16