Metered concurrency: Difference between revisions
Content added Content deleted
(omit m4) |
m (Fixed lang tags.) |
||
Line 6: | Line 6: | ||
The interface for the counting semaphore is defined in an Ada package specification: |
The interface for the counting semaphore is defined in an Ada package specification: |
||
<lang ada> |
<lang ada>package Semaphores is |
||
protected type Counting_Semaphore(Max : Positive) is |
|||
entry Acquire; |
|||
procedure Release; |
|||
function Count return Natural; |
|||
private |
|||
Lock_Count : Natural := 0; |
|||
end Counting_Semaphore; |
|||
end Semaphores;</lang> |
|||
The ''Acquire'' entry has a condition associated with it. A task can only execute the ''Acquire'' entry when ''Lock_Count'' is less than ''Max''. This is the key to making this structure behave as a counting semaphore. This condition, and all the other aspects of ''Counting_Semaphore'' are contained in the package body. |
The ''Acquire'' entry has a condition associated with it. A task can only execute the ''Acquire'' entry when ''Lock_Count'' is less than ''Max''. This is the key to making this structure behave as a counting semaphore. This condition, and all the other aspects of ''Counting_Semaphore'' are contained in the package body. |
||
<lang ada> |
<lang ada>package body Semaphores is |
||
------------------------ |
|||
-- Counting_Semaphore -- |
|||
------------------------ |
|||
protected body Counting_Semaphore is |
|||
------------- |
|||
-- Acquire -- |
|||
------------- |
|||
entry Acquire when Lock_Count < Max is |
|||
begin |
|||
Lock_Count := Lock_Count + 1; |
|||
end Acquire; |
|||
----------- |
|||
-- Count -- |
|||
----------- |
|||
function Count return Natural is |
|||
begin |
|||
return Lock_Count; |
|||
end Count; |
|||
------------- |
|||
-- Release -- |
|||
------------- |
|||
procedure Release is |
|||
begin |
|||
if Lock_Count > 0 then |
|||
Lock_Count := Lock_Count - 1; |
|||
end if; |
|||
end Release; |
|||
end Counting_Semaphore; |
|||
end Semaphores;</lang> |
|||
We now need a set of tasks to properly call an instance of ''Counting_Semaphore''. |
We now need a set of tasks to properly call an instance of ''Counting_Semaphore''. |
||
<lang ada> |
<lang ada>with Semaphores; |
||
with Ada.Text_Io; use Ada.Text_Io; |
|||
procedure Semaphores_Main is |
|||
-- Create an instance of a Counting_Semaphore with Max set to 3 |
|||
Lock : Semaphores.Counting_Semaphore(3); |
|||
-- Define a task type to interact with the Lock object declared above |
|||
task type Worker is |
|||
entry Start (Sleep : in Duration; Id : in Positive); |
|||
end Worker; |
|||
task body Worker is |
|||
Sleep_Time : Duration; |
|||
My_Id : Positive; |
|||
begin |
|||
accept Start(Sleep : in Duration; Id : in Positive) do |
|||
My_Id := Id; |
|||
Sleep_Time := Sleep; |
|||
end Start; |
|||
--Acquire the lock. The task will suspend until the Acquire call completes |
|||
Lock.Acquire; |
|||
Put_Line("Task #" & Positive'Image(My_Id) & " acquired the lock."); |
|||
-- Suspend the task for Sleep_Time seconds |
|||
delay Sleep_Time; |
|||
-- Release the lock. Release is unconditional and happens without suspension |
|||
Lock.Release; |
|||
end Worker; |
|||
-- Create an array of 5 Workers |
|||
type Staff is array(Positive range 1..5) of Worker; |
|||
Crew : Staff; |
|||
begin |
|||
for I in Crew'range loop |
|||
Crew(I).Start(2.0, I); |
|||
end loop; |
|||
end Semaphores_Main;</lang> |
|||
=={{header|ALGOL 68}}== |
=={{header|ALGOL 68}}== |
||
<lang algol68>SEMA sem = LEVEL 1; |
|||
PROC job = (INT n)VOID: ( |
|||
printf(($" Job "d" acquired Semaphore ..."$,n)); |
|||
TO 10000000 DO SKIP OD; |
|||
printf(($" Job "d" releasing Semaphore"l$,n)) |
|||
); |
|||
PAR ( |
|||
( DOWN sem ; job(1) ; UP sem ) , |
|||
( DOWN sem ; job(2) ; UP sem ) , |
|||
( DOWN sem ; job(3) ; UP sem ) |
|||
⚫ | |||
) |
|||
Output: |
Output: |
||
<lang algol68>Job 1 acquired Semaphore ... Job 1 releasing Semaphore |
|||
Job 3 acquired Semaphore ... Job 3 releasing Semaphore |
|||
Job 2 acquired Semaphore ... Job 2 releasing Semaphore</lang> |
|||
=={{header|C}}== |
=={{header|C}}== |
||
Line 259: | Line 259: | ||
===Phobos with tools=== |
===Phobos with tools=== |
||
Using the scrapple.tools extension library for Phobos .. |
Using the scrapple.tools extension library for Phobos .. |
||
<lang d> |
<lang d>module metered; |
||
module metered; |
|||
import tools.threads, tools.log, tools.time, tools.threadpool; |
import tools.threads, tools.log, tools.time, tools.threadpool; |
||
Line 280: | Line 279: | ||
for (int i = 0; i < 10; ++i) |
for (int i = 0; i < 10; ++i) |
||
done.acquire; |
done.acquire; |
||
}</lang> |
|||
} |
|||
⚫ | |||
=={{header|E}}== |
=={{header|E}}== |
||
This semaphore slightly differs from the task description; the release operation is not on the semaphore itself but given out with each acquisition, and cannot be invoked too many times. |
This semaphore slightly differs from the task description; the release operation is not on the semaphore itself but given out with each acquisition, and cannot be invoked too many times. |
||
<lang e>def makeSemaphore(maximum :(int > 0)) { |
|||
var current := 0 |
|||
def waiters := <elib:vat.makeQueue>() |
|||
def notify() { |
|||
while (current < maximum && waiters.hasMoreElements()) { |
|||
current += 1 |
|||
waiters.optDequeue().resolve(def released) |
|||
when (released) -> { |
|||
current -= 1 |
|||
notify() |
|||
} |
|||
} |
|||
} |
|||
def semaphore { |
|||
to acquire() { |
|||
waiters.enqueue(def response) |
|||
notify() |
|||
return response |
|||
} |
|||
to count() { return current } |
|||
} |
|||
return semaphore |
|||
} |
|||
def work(label, interval, semaphore, timer, println) { |
|||
when (def releaser := semaphore <- acquire()) -> { |
|||
println(`$label: I have acquired the lock.`) |
|||
releaser.resolve( |
|||
timer.whenPast(timer.now() + interval, fn { |
|||
println(`$label: I will have released the lock.`) |
|||
}) |
|||
) |
|||
} |
|||
} |
|||
def semaphore := makeSemaphore(3) |
|||
for i in 1..5 { |
|||
work(i, 2000, semaphore, timer, println) |
|||
}</lang> |
|||
} |
|||
=={{header|Java}}== |
=={{header|Java}}== |
||
Line 392: | Line 390: | ||
Python threading module includes a semaphore implementation. This code show how to use it. |
Python threading module includes a semaphore implementation. This code show how to use it. |
||
⚫ | |||
<pre> |
|||
⚫ | |||
import threading |
import threading |
||
Line 429: | Line 426: | ||
running = 0 |
running = 0 |
||
for t in workers: |
for t in workers: |
||
t.join() |
t.join()</lang> |
||
</pre> |
|||
=={{header|Raven}}== |
=={{header|Raven}}== |
||
Counting semaphores are built in: |
Counting semaphores are built in: |
||
<lang raven># four workers may be concurrent |
|||
4 semaphore as sem |
|||
thread worker |
|||
5 each as i |
|||
sem acquire |
|||
# tid is thread id |
|||
tid "%d acquired semaphore\n" print |
|||
2000 ms |
|||
sem release |
|||
# let others acquire |
|||
100 ms |
|||
# start 10 threads |
|||
group |
|||
10 each drop worker |
|||
list as workers</lang> |
|||
Thread joining is automatic by default. |
Thread joining is automatic by default. |
||
Line 526: | Line 522: | ||
The number of concurrent jobs can be set by issuing that many echo '1''s at the begining to sem. |
The number of concurrent jobs can be set by issuing that many echo '1''s at the begining to sem. |
||
<lang bash>rm -f sem ; mkfifo sem |
|||
acquire() { |
|||
x='';while test -z "$x"; do read x; done; |
|||
} |
|||
release() { |
|||
echo '1' |
|||
} |
|||
job() { |
|||
n=$1; echo "Job $n acquired Semaphore">&2 ; sleep 2; echo "Job $n released Semaphore">&2 ; |
|||
} |
|||
( acquire < sem ; job 1 ; release > sem ) & |
|||
( acquire < sem ; job 2 ; release > sem ) & |
|||
( acquire < sem ; job 3 ; release > sem ) & |
|||
echo 'Initialize Jobs' >&2 ; echo '1' > sem</lang> |
|||
=={{header|Visual Basic .NET}}== |
=={{header|Visual Basic .NET}}== |
||
Line 550: | Line 546: | ||
This code shows using a local semaphore. Semaphores can also be named, in which case they will be shared system wide. |
This code shows using a local semaphore. Semaphores can also be named, in which case they will be shared system wide. |
||
<lang vbnet>Dim sem As New Semaphore(5, 5) 'Indicates that up to 5 resources can be aquired |
|||
sem.WaitOne() 'Blocks until a resouce can be aquired |
|||
Dim oldCount = sem.Release() 'Returns a resource to the pool |
|||
'oldCount has the Semaphore's count before Release was called</lang> |
|||
{{omit from|TI-83 BASIC}} {{omit from|TI-89 BASIC}} <!-- Does not have concurrency or background processes. --> |
{{omit from|TI-83 BASIC}} {{omit from|TI-89 BASIC}} <!-- Does not have concurrency or background processes. --> |