View Problem
Create read/write lock on a shared resource.
Create multiple threads or processes who are either readers or writers. There should be more readers then writers.
(From Wikipedia):
Multiple readers can read the data in parallel but an exclusive lock is needed while writing the data. When a writer is writing the data, readers will be blocked until the writer is finished writing.
Example:
-Output-
Thread one says that the value is 8.
Thread three says that the value is 8.
Thread two is taking the lock.
Thread four tried to read the value, but could not.
Thread five tried to write to the value, but could not.
Thread two is changing the value to 9.
Thread two is releasing the lock.
Thread four says that the value is 9.
...
--Notice that when a needed resource is locked, a thread can set a timer and try again in the future, or wait to be notified that the resource is no longer locked.
Submit a new solution for clojure, cpp, erlang, fantom ...
There is 1 other solution in java
(From Wikipedia):
Multiple readers can read the data in parallel but an exclusive lock is needed while writing the data. When a writer is writing the data, readers will be blocked until the writer is finished writing.
Example:
-Output-
Thread one says that the value is 8.
Thread three says that the value is 8.
Thread two is taking the lock.
Thread four tried to read the value, but could not.
Thread five tried to write to the value, but could not.
Thread two is changing the value to 9.
Thread two is releasing the lock.
Thread four says that the value is 9.
...
--Notice that when a needed resource is locked, a thread can set a timer and try again in the future, or wait to be notified that the resource is no longer locked.
clojure
; NOTE! Using explicit locking is NOT the Clojure way. It was done
; this way in order to comply exactly with the problem
; specification. Sharing data in Clojure would normally be done by
; using "atom", "agent" or "ref" depending on situation. None of those
; methods would ever result in the reader not being able to read (as
; required by the problem) since reading is wait-free in clojure.
(def *readers* (map #(agent %) '("one" "two" "three")))
(def *writers* (map #(agent %) '("four" "five")))
(def *mutex* (agent :unlocked))
(def *value* 0)
; mutex implementation
(defn lock [state who success-fn fail-fn]
(send who (if (= state :locked) fail-fn success-fn))
:locked)
(defn unlock [mutex]
:unlocked)
; Must be invoked with send-off since this handler blocks
(defn rand-sleep [state next-fn]
(Thread/sleep (rand-int 5))
(send *agent* next-fn)
state)
; Reader functions
(declare try-read)
(defn reader-got-lock [name]
(println (format "Thread %s says that the value is %d." name *value*))
(send *mutex* unlock)
(send-off *agent* rand-sleep try-read)
name)
(defn reader-did-not-get-lock [name]
(println (format "Thread %s tried to read the value, but could not." name))
(send-off *agent* rand-sleep try-read)
name)
(defn try-read [name]
(send *mutex* lock *agent* reader-got-lock reader-did-not-get-lock)
name)
; Writer functions
(declare try-write)
(defn writer-got-lock [name]
(println (format "Thread %s is taking the lock." name))
(def *value* (rand-int 10))
(println (format "Thread %s is changing the value to %d." name *value*))
(send *mutex* unlock)
(println (format "Thread %s is relasing the lock." name))
(send-off *agent* rand-sleep try-write)
name)
(defn writer-did-not-get-lock [name]
(println (format "Thread %s tried to write the value, but could not." name))
(send-off *agent* rand-sleep try-write)
name)
(defn try-write [name]
(send *mutex* lock *agent* writer-got-lock writer-did-not-get-lock)
name)
(dorun (map #(send % try-write) *writers*))
(dorun (map #(send % try-read) *readers*))
; this way in order to comply exactly with the problem
; specification. Sharing data in Clojure would normally be done by
; using "atom", "agent" or "ref" depending on situation. None of those
; methods would ever result in the reader not being able to read (as
; required by the problem) since reading is wait-free in clojure.
(def *readers* (map #(agent %) '("one" "two" "three")))
(def *writers* (map #(agent %) '("four" "five")))
(def *mutex* (agent :unlocked))
(def *value* 0)
; mutex implementation
(defn lock [state who success-fn fail-fn]
(send who (if (= state :locked) fail-fn success-fn))
:locked)
(defn unlock [mutex]
:unlocked)
; Must be invoked with send-off since this handler blocks
(defn rand-sleep [state next-fn]
(Thread/sleep (rand-int 5))
(send *agent* next-fn)
state)
; Reader functions
(declare try-read)
(defn reader-got-lock [name]
(println (format "Thread %s says that the value is %d." name *value*))
(send *mutex* unlock)
(send-off *agent* rand-sleep try-read)
name)
(defn reader-did-not-get-lock [name]
(println (format "Thread %s tried to read the value, but could not." name))
(send-off *agent* rand-sleep try-read)
name)
(defn try-read [name]
(send *mutex* lock *agent* reader-got-lock reader-did-not-get-lock)
name)
; Writer functions
(declare try-write)
(defn writer-got-lock [name]
(println (format "Thread %s is taking the lock." name))
(def *value* (rand-int 10))
(println (format "Thread %s is changing the value to %d." name *value*))
(send *mutex* unlock)
(println (format "Thread %s is relasing the lock." name))
(send-off *agent* rand-sleep try-write)
name)
(defn writer-did-not-get-lock [name]
(println (format "Thread %s tried to write the value, but could not." name))
(send-off *agent* rand-sleep try-write)
name)
(defn try-write [name]
(send *mutex* lock *agent* writer-got-lock writer-did-not-get-lock)
name)
(dorun (map #(send % try-write) *writers*))
(dorun (map #(send % try-read) *readers*))
cpp boost
class reader
{
string name_;
public:
reader(const string& name) : name_(name) {}
void operator()() {
for (;;this_thread::sleep(posix_time::milliseconds(1)))
{
shared_lock<shared_mutex> lock(m, try_to_lock);
lock_guard<mutex> cout_lock(io_m);
cout << "Thread " << name_;
if (lock)
cout << " says that the value is " << shared_value << "." << endl;
else
cout << " tried to read the value, but could not." << endl;
}
}
};
class writer
{
string name_;
public:
writer(const string& name) : name_(name) {}
void operator()() {
for (;;this_thread::sleep(posix_time::milliseconds(1)))
{
unique_lock<shared_mutex> lock(m, try_to_lock);
lock_guard<mutex> cout_lock(io_m);
cout << "Thread " << name_;
if (lock)
{
cout << " is taking the lock." << endl;
shared_value = rand() % 10;
cout << "Thread " << name_ << " is changing the value to " << shared_value << endl;
cout << "Thread " << name_ << " is releasing the lock. " << endl;
}
else
cout << " tried to write to the value, but could not." << endl;
}
}
};
int main()
{
thread t1 = thread(reader("one"));
thread t2 = thread(reader("two"));
thread t3 = thread(reader("three"));
thread t4 = thread(writer("four"));
writer("five")();
}
{
string name_;
public:
reader(const string& name) : name_(name) {}
void operator()() {
for (;;this_thread::sleep(posix_time::milliseconds(1)))
{
shared_lock<shared_mutex> lock(m, try_to_lock);
lock_guard<mutex> cout_lock(io_m);
cout << "Thread " << name_;
if (lock)
cout << " says that the value is " << shared_value << "." << endl;
else
cout << " tried to read the value, but could not." << endl;
}
}
};
class writer
{
string name_;
public:
writer(const string& name) : name_(name) {}
void operator()() {
for (;;this_thread::sleep(posix_time::milliseconds(1)))
{
unique_lock<shared_mutex> lock(m, try_to_lock);
lock_guard<mutex> cout_lock(io_m);
cout << "Thread " << name_;
if (lock)
{
cout << " is taking the lock." << endl;
shared_value = rand() % 10;
cout << "Thread " << name_ << " is changing the value to " << shared_value << endl;
cout << "Thread " << name_ << " is releasing the lock. " << endl;
}
else
cout << " tried to write to the value, but could not." << endl;
}
}
};
int main()
{
thread t1 = thread(reader("one"));
thread t2 = thread(reader("two"));
thread t3 = thread(reader("three"));
thread t4 = thread(writer("four"));
writer("five")();
}
fsharp
open System.Threading
let lock = new ReaderWriterLock()
let mutable value = 0
let lockTimeout = 1
let ReaderThread t =
let random = new System.Random()
for i in 0 .. 100 do
try
lock.AcquireReaderLock(lockTimeout)
try
printfn "Thread %i says that the value is %i" t value
finally
lock.ReleaseReaderLock()
with _ ->
printfn "Thread %i tried to read the value, but could not (timeout)." t
Thread.Sleep(random.Next(50))
let WriterThread t =
let random = new System.Random()
for i in 0 .. 100 do
try
lock.AcquireWriterLock(lockTimeout)
try
value <- random.Next(10)
printfn "Thread %i is changing the value to %i" t value
Thread.MemoryBarrier()
finally
lock.ReleaseWriterLock()
printfn "Thread %i is releasing the lock." t
with _ ->
printfn "Thread %i tried to write the value, but could not (timeout)." t
Thread.Sleep(random.Next(50))
[| 0 .. 20 |]
|> Array.iter (fun t ->
async {
if t % 3 = 0 then
WriterThread t
else
ReaderThread t
}
|> Async.Start
)
let lock = new ReaderWriterLock()
let mutable value = 0
let lockTimeout = 1
let ReaderThread t =
let random = new System.Random()
for i in 0 .. 100 do
try
lock.AcquireReaderLock(lockTimeout)
try
printfn "Thread %i says that the value is %i" t value
finally
lock.ReleaseReaderLock()
with _ ->
printfn "Thread %i tried to read the value, but could not (timeout)." t
Thread.Sleep(random.Next(50))
let WriterThread t =
let random = new System.Random()
for i in 0 .. 100 do
try
lock.AcquireWriterLock(lockTimeout)
try
value <- random.Next(10)
printfn "Thread %i is changing the value to %i" t value
Thread.MemoryBarrier()
finally
lock.ReleaseWriterLock()
printfn "Thread %i is releasing the lock." t
with _ ->
printfn "Thread %i tried to write the value, but could not (timeout)." t
Thread.Sleep(random.Next(50))
[| 0 .. 20 |]
|> Array.iter (fun t ->
async {
if t % 3 = 0 then
WriterThread t
else
ReaderThread t
}
|> Async.Start
)
groovy
def lock = new ReentrantLock()
Integer value = 8
20.times { i ->
if (i % 3 == 0) {
Thread.start {
if (!lock.tryLock()) {
println "Thread " + i + " tried to write the value, but could not."
lock.lock()
}
value = (int) (Math.random() * 10)
println "Thread " + i + " is changing the value to " + value
lock.unlock()
println "Thread " + i + " is releasing the lock."
}
} else {
Thread.start {
if (!lock.tryLock()) {
println "Thread " + i + " tried to read the value, but could not."
lock.lock()
}
println "Thread " + i + " says that the value is " + value + "."
lock.unlock()
}
}
}
Integer value = 8
20.times { i ->
if (i % 3 == 0) {
Thread.start {
if (!lock.tryLock()) {
println "Thread " + i + " tried to write the value, but could not."
lock.lock()
}
value = (int) (Math.random() * 10)
println "Thread " + i + " is changing the value to " + value
lock.unlock()
println "Thread " + i + " is releasing the lock."
}
} else {
Thread.start {
if (!lock.tryLock()) {
println "Thread " + i + " tried to read the value, but could not."
lock.lock()
}
println "Thread " + i + " says that the value is " + value + "."
lock.unlock()
}
}
}
ocaml
(* Compilation (native):
$ ocamlopt -thread unix.cmxa threads.cmxa threads_lock.ml -o threads_lock
*)
let value = ref 8
let mutex = Mutex.create ()
let create_writer i =
if not (Mutex.try_lock mutex) then begin
Printf.printf "Thread %d tried to write the value but could not.\n" i;
Mutex.lock mutex
end;
value := Random.int 10;
Printf.printf "Thread %d is changing the value to %d\n" i !value;
Mutex.unlock mutex;
Printf.printf "Thread %d is releasing the lock.\n" i
let create_reader i =
if not (Mutex.try_lock mutex) then begin
Printf.printf "Thread %d tried to read the value but could not.\n" i;
Mutex.lock mutex
end;
Printf.printf "Thread %d says that the value is %d\n" i !value;
Mutex.unlock mutex
;;
let thread_ids = Array.init 20 (fun i ->
Thread.create (if i mod 3 == 0 then create_writer else create_reader) i) in
Array.iter Thread.join thread_ids
(* Compilation (native):
$ ocamlopt -thread unix.cmxa threads.cmxa threads_lock.ml -o threads_lock
*)
let value = ref 8
let mutex = Mutex.create ()
let create_writer i =
if not (Mutex.try_lock mutex) then begin
Printf.printf "Thread %d tried to write the value but could not.\n" i;
Mutex.lock mutex
end;
value := Random.int 10;
Printf.printf "Thread %d is changing the value to %d\n" i !value;
Mutex.unlock mutex;
Printf.printf "Thread %d is releasing the lock.\n" i
let create_reader i =
if not (Mutex.try_lock mutex) then begin
Printf.printf "Thread %d tried to read the value but could not.\n" i;
Mutex.lock mutex
end;
Printf.printf "Thread %d says that the value is %d\n" i !value;
Mutex.unlock mutex
;;
let thread_ids = Array.init 20 (fun i ->
Thread.create (if i mod 3 == 0 then create_writer else create_reader) i) in
Array.iter Thread.join thread_ids
python
#!/usr/bin/python
from threading import Thread, Lock
import time
thread_readers = ['one','two','three']
thread_writer = ['four','five']
lock = Lock()
value = 0
def Threadread(number):
global value
while True:
if lock.acquire(False):
print "Thread", number, "is taking the lock"
value += 1
print "Thread", number, "is changing the value to", value
print "Thread", number, "is releasing the lock."
lock.release()
else:
print "Thread", number, "tried to write to the value, but could not."
def Threadwrite(number):
global value
while True:
if lock.acquire(False):
print "Thread", number ,"four says that the value is", value
else:
print "Thread", number ,"tried to read the value, but could not."
if __name__ == "__main__":
for n in range(0,len(thread_readers)):
th =Thread(target=Threadread, args=(thread_readers[n],))
th.start()
for n in range(0,len(thread_writer)):
th =Thread(target=Threadwrite, args=(thread_writer[n],))
th.start()
from threading import Thread, Lock
import time
thread_readers = ['one','two','three']
thread_writer = ['four','five']
lock = Lock()
value = 0
def Threadread(number):
global value
while True:
if lock.acquire(False):
print "Thread", number, "is taking the lock"
value += 1
print "Thread", number, "is changing the value to", value
print "Thread", number, "is releasing the lock."
lock.release()
else:
print "Thread", number, "tried to write to the value, but could not."
def Threadwrite(number):
global value
while True:
if lock.acquire(False):
print "Thread", number ,"four says that the value is", value
else:
print "Thread", number ,"tried to read the value, but could not."
if __name__ == "__main__":
for n in range(0,len(thread_readers)):
th =Thread(target=Threadread, args=(thread_readers[n],))
th.start()
for n in range(0,len(thread_writer)):
th =Thread(target=Threadwrite, args=(thread_writer[n],))
th.start()
scala
import java.util.concurrent.locks.ReentrantReadWriteLock
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock
import scala.util.Random
class Reader(name: String, lock: ReadLock) extends Thread {
override def run() = {
println(name)
while (true) {
if (!lock.tryLock)
{
println("Thread " + name + " tried to read the value, but could not.")
lock.lock
}
println("Thread " + name + " says that the value is " + rwLockOnSharedResource.value)
lock.unlock
Thread.sleep(3) // Generates output more similar to the problem description
}
}
}
class Writer(name: String, lock: WriteLock) extends Thread {
override def run() = {
while (true) {
if (!lock.tryLock) {
println("Thread " + name + " tried to write the value, but could not.")
lock.lock
}
println("Thread " + name + " is taking the lock.")
rwLockOnSharedResource.value = rwLockOnSharedResource.nextValue
println("Thread " + name + " is changing the value to " + rwLockOnSharedResource.value)
lock.unlock
println("Thread " + name + " is releasing the lock.")
Thread.sleep(3) // Generates output more similar to the problem description
}
}
}
object rwLockOnSharedResource {
private val maxValue = 10
private val randomVal = new Random
var value = nextValue
def nextValue = randomVal.nextInt(maxValue)
def main(args: Array[String]) = {
val rwLock = new ReentrantReadWriteLock(true)
val threadNames = List("one", "two", "three", "four", "five")
val readerCnt = threadNames.length * 2 / 3
val readerNames = threadNames.take(readerCnt)
val writerNames = threadNames.drop(readerCnt)
readerNames.foreach(new Reader(_, rwLock.readLock).start)
writerNames.foreach(new Writer(_, rwLock.writeLock).start)
}
}
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock
import scala.util.Random
class Reader(name: String, lock: ReadLock) extends Thread {
override def run() = {
println(name)
while (true) {
if (!lock.tryLock)
{
println("Thread " + name + " tried to read the value, but could not.")
lock.lock
}
println("Thread " + name + " says that the value is " + rwLockOnSharedResource.value)
lock.unlock
Thread.sleep(3) // Generates output more similar to the problem description
}
}
}
class Writer(name: String, lock: WriteLock) extends Thread {
override def run() = {
while (true) {
if (!lock.tryLock) {
println("Thread " + name + " tried to write the value, but could not.")
lock.lock
}
println("Thread " + name + " is taking the lock.")
rwLockOnSharedResource.value = rwLockOnSharedResource.nextValue
println("Thread " + name + " is changing the value to " + rwLockOnSharedResource.value)
lock.unlock
println("Thread " + name + " is releasing the lock.")
Thread.sleep(3) // Generates output more similar to the problem description
}
}
}
object rwLockOnSharedResource {
private val maxValue = 10
private val randomVal = new Random
var value = nextValue
def nextValue = randomVal.nextInt(maxValue)
def main(args: Array[String]) = {
val rwLock = new ReentrantReadWriteLock(true)
val threadNames = List("one", "two", "three", "four", "five")
val readerCnt = threadNames.length * 2 / 3
val readerNames = threadNames.take(readerCnt)
val writerNames = threadNames.drop(readerCnt)
readerNames.foreach(new Reader(_, rwLock.readLock).start)
writerNames.foreach(new Writer(_, rwLock.writeLock).start)
}
}
Submit a new solution for clojure, cpp, erlang, fantom ...
There is 1 other solution in java




