Producer
Different Thread with its own SynchronizedBlockingQueue.
Each producer put there message into its own queue.
Consumer
Different Thread which will get message from either from any one of queue and start process.
Now for communicating producer and consumer, we need broker. which may be bottleneck. Is there any other way consumer get one message form any producer and start process.
Since you did not specify a language, I thought I would provide a general example using the Ada programming language. This example has the consumer simply print the messages from the producers, but it gives the producer-consumer architecture you described.
with Ada.Task_Identification; use Ada.Task_Identification;
package Multiple_Producer is
type Producer_Message is private;
protected type Buffer is
entry Set_Message (Item : in Producer_Message);
entry Get_Message (Item : out Producer_Message);
private
Msg : Producer_Message;
Is_New : Boolean := False;
end Buffer;
type Buf_Alias is access all Buffer;
type Buf_Array is array (Positive range <>) of aliased Buffer;
type Buf_Access is access all Buf_Array;
task type Producer is
entry Set_Buffer (Item : Buf_Alias);
entry Stop;
end Producer;
task Consumer is
entry Set_Buffers (Item : Buf_Access);
entry Stop;
end Consumer;
private
type Producer_Message is record
the_Task : Task_Id;
Value : Integer;
end record;
end Multiple_Producer;
with Ada.Text_IO; use Ada.Text_IO;
package body Multiple_Producer is
--------------
-- Producer --
--------------
task body Producer is
Message : Producer_Message := (Current_Task, 0);
The_Buf : Buf_Alias;
begin
accept Set_Buffer(Item : in Buf_Alias) do
The_Buf := Item;
end Set_Buffer;
loop
select
accept Stop;
exit;
else
delay 0.01;
The_Buf.Set_Message(Message);
Message.Value := Message.Value + 1;
end select;
end loop;
end Producer;
--------------
-- Consumer --
--------------
task body Consumer is
Message : Producer_Message;
Buffers : Buf_Access;
begin
accept Set_Buffers(Item : Buf_Access) do
Buffers := Item;
end Set_Buffers;
loop
select
accept Stop;
exit;
else
-- Poll the buffers
for I in Buffers'Range loop
select
Buffers(I).Get_Message(Message);
Put_Line(Image(Message.The_Task) & ": " &
Integer'Image(Message.Value));
or
delay 0.001;
end select;
end loop;
end select;
end loop;
end Consumer;
------------
-- Buffer --
------------
protected body Buffer is
-----------------
-- Set_Message --
-----------------
entry Set_Message (Item : in Producer_Message) when not Is_New is
begin
Msg := Item;
Is_New := True;
end Set_Message;
-----------------
-- Get_Message --
-----------------
entry Get_Message (Item : out Producer_Message) when Is_New is
begin
Item := Msg;
Is_New := False;
end Get_Message;
end Buffer;
end Multiple_Producer;
with Multiple_Producer; use Multiple_Producer;
procedure Main is
subtype Producer_Range is Positive range 1..5;
The_Producers : array(Producer_Range) of Producer;
The_Buffers : Buf_Access := new Buf_Array(Producer_Range);
begin
for I in Producer_Range loop
The_Producers(I).Set_Buffer(The_Buffers(I)'Access);
end loop;
Consumer.Set_Buffers(The_Buffers);
delay 4.0;
for P of The_Producers loop
P.Stop;
end loop;
Consumer.Stop;
end Main;
Related
I'm just starting to learn Ada 95 and I'm having some problems with comparing strings.
Here's the code:
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
with Ada.Command_Line;
with Ada.Strings.Unbounded;
procedure Test1 is
vad : String(1..9);
Amount : Integer;
Main : Integer;
Second : Integer;
Third : Integer;
begin
Main := 1;
Second := 0;
Third := 0;
Put("What do you want to do?");
New_Line(1);
Get(vad);
New_Line(1);
if Vad = "fibonacci" then
Put("How long do you want the sequence to be");
New_Line(1);
Get(Amount);
New_Line(1);
Amount := Amount -1;
for I in 1 .. Amount loop
Put(Main);
New_Line(1);
--Put(" ");
Third := Second;
Second := Main;
Main := (Second + third);
end loop;
New_Line(2);
elsif Vad = "two" then
Put("How long do you want the sequence to be?");
New_Line(1);
Get(Amount);
New_Line(1);
for U in 1 .. Amount loop
Put(U * 2);
Put(", ");
end loop;
else
Put("ok");
end if;
end Test1;
As it is now, the if statement recognises when I type fibonacci, but when I type two it just goes to the 'else' part of the code.
Any ideas what might be wrong?
Thanks in advance.
What probably confuses you is that the type String is actually a fixed-length string.
This means that comparing Vad with any string, which isn't exactly 9 characters long will fail.
One option is to declare Vad and initialise it with the output from Ada.Text_IO.Get_Line in one go:
Vad : constant String := Ada.Text_IO.Get_Line;
This way you will read exactly what is written until (and not including) the next line-break entered into Vad.
Jacob's explanation is correct. Here's your code modified to use Ada.Text_IO.Get_Line
I removed the "with Ada.Strings.Unbounded" line because the package is not needed.
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
with Ada.Command_Line;
procedure Test1 is
Amount : Integer;
Main : Integer := 1;
Second : Integer := 0;
Third : Integer := 0;
begin
Put_Line("What do you want to do?");
declare
vad : constant string := Get_Line;
begin
if Vad = "fibonacci" then
Put_Line("How long do you want the sequence to be?");
Get(Amount);
Amount := Amount -1;
for I in 1 .. Amount loop
Put(Main);
New_Line;
--Put(" ");
Third := Second;
Second := Main;
Main := (Second + third);
end loop;
New_Line(2);
elsif Vad = "two" then
Put_Line("How long do you want the sequence to be?");
Get(Amount);
for U in 1 .. Amount loop
Put(integer'image(U * 2) & ", ");
end loop;
else
Put("ok");
end if;
end;
end Test1;
I have an Inno Setup script which calls a DLL.
The DLL starts a thread which in the end calls a function pointer in the Inno Setup.
Since i don't want to change the logic of my Inno Script to much i would like to use a semaphore or something like that.
The important part in here is that the gui shouldn't be blocked.
here a little snippet of my code
procedure InstallData();
var
arrComponents : TStringList;
i, index, p : Integer;
countComp : Integer;
begin
countComp := ICountComponents();
pbState.Max:= countComp;
arrComponents := IGetComponentsToInstall();
pbState.Max := countComp;
for i := 0 to countComp-1 do
begin
// lock semaphore
pbState.Position := i;
p := Pos(' ', arrComponents[i]);
if p > 0 then
begin
//Unzip component
//Call the DLL
end
else
begin
//unzip something else
//Call the DLL
end
end
end;
procedure ProgressCallback(progress:Integer);
begin
pbStateZip.Position:= progress;
//if progress = 100
// unlock semaphore
//
end;
Are there semaphores or is there an equivalent to this which does not block my GUI ?
I want to read blobfield (with blobstream) from client side (over network) but application freezes while fetching data. How can I read blobfield without freezing and showing percentage with a progressbar. (I'm using Delphi and Firebird)
i'm using uniquery component. i've found this code from: http://forums.devart.com/viewtopic.php?t=14629
but it doesn't work properly:
const
BlockSize= $F000;
var
Blob: TBlob;
Buffer: array of byte;
p: pointer;
pos, count: integer;
UniQuery1.SQL.Text:= 'select * from TABLE1 where FIELD_ID = 1';
UniQuery1.Open;
blob:= uniquery1.GetBlob('DATA');
SetLength(buffer, blob.Size);
ProgressBar1.Position:= 0;
Application.ProcessMessages;
repeat
count:= Blob.Read(pos, blocksize, p);
ProgressBar1.Position:= Round(pos/Blob.Size * 100);
pos:= pos + count;
p:= pointer(integer(p) + count);
Application.ProcessMessages;
until count < blocksize;
PS: i've set uniquery's options:
cacheblobs:= false;
streamedblobls:= true;
deferredblobread:= true;
in the first step of repeat-until loop, Blob.Read method reads all of stream, so it doesnt work properly.
You should use a thread, here is an example with Delphi TThread:
type
TMyForm = class(TForm)
private
FPosition: Integer;
procedure ProgressUpdate;
procedure Execute;
end;
procedure TMyForm.ProgressUpdate;
begin
ProgressBar1.Position := FPosition;
end;
procedure TMyForm.Execute;
begin
FPosition:= 0;
ProgressUpdate;
Thread := TThread.CreateAnonymousThread(procedure
begin
repeat
// Do some long running stuff (in chunks, so we can update the position)
FPosition := CalculatePosition;
// Important: Synchronize will run ProgressUpdate in the main thread!
TThread.Synchronize(nil, ProgressUpdate);
until SomeCondition;
end
);
Thread.Start;
end;
So after applying this pattern to your code we get:
type
TMyForm = class(TForm)
private
FPosition: Integer;
procedure ProgressUpdate;
procedure Execute;
end;
procedure TMyForm.ProgressUpdate;
begin
ProgressBar1.Position := FPosition;
end;
procedure TMyForm.Execute;
var
Blob: TBlob;
Thread: TThread;
begin
UniQuery1.SQL.Text := 'SELECT * FROM TABLE1 WHERE FIELD_ID = 1';
UniQuery1.Open;
Blob := UniQuery1.GetBlob('DATA');
FPosition:= 0;
ProgressUpdate;
Thread := TThread.CreateAnonymousThread(
procedure
const
BlockSize = $F000;
var
Buffer: array of Byte;
P: Pointer;
Pos, Count: Integer;
begin
SetLength(Buffer, Blob.Size);
repeat
Count := Blob.Read(Pos, BlockSize, P);
FPosition := Round(Pos / Blob.Size * 100);
Pos := Pos + Count;
P := Pointer(Integer(P) + Count);
// Important: Synchronize will run ProgressUpdate in the main thread!
TThread.Synchronize(nil, ProgressUpdate);
until Count < BlockSize;
end
);
Thread.Start;
end;
I removed the Application.ProcessMessage and moved all processing to the thread.
The Thread is setting the FPosition private attribute and uses TThread.Synchronize to set the ProgressBar position to FPosition in the main thread.
If your block size is not big enough this might still block the UI (due to excessive synchronization), so choose an appropriate block size or add some update delay.
You have to make sure that the connection of the UniQuery1 object is not used in the main thread while the anonymous thread is running or move the connection and query to the thread as well.
Also this can have reentrance problems, but it should give you a basic idea of how to use a thread for background processing.
PS: It might also be a good idea to run the query in the thread, especially if it can take some time.
In my Delphi project, I derive a thread class TMyThread, and follow the advice from forums to use AllocateHWnd to create a window handle. In TMyThread object, I call SendMessage to send message to the window handle.
When the messages sent are in small volume, then the application works well. However, when the messages are in large volume, the application will deadlock and lose responses. I think may be the message queue is full as in LogWndProc, there are only codes to process the message, but no codes to remove the messages from the queue, that may cause all the processed messages still exist in the queue and the queue becomes full. Is that correct?
The codes are attached below:
var
hLogWnd: HWND = 0;
procedure TForm1.FormCreate(Sender: TObject);
begin
hLogWnd := AllocateHWnd(LogWndProc);
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
if hLogWnd <> 0 then
DeallocateHWnd(hLogWnd);
end;
procedure TForm1.LogWndProc(var Message: TMessage);
var
S: PString;
begin
if Message.Msg = WM_UPDATEDATA then
begin
S := PString(msg.LParam);
try
List1.Items.Add(S^);
finally
Dispose(S);
end;
end else
Message.Result := DefWindowProc(hLogWnd, Message.Msg, Message.WParam,
Message.LParam);
end;
procedure TMyThread.SendLog(I: Integer);
var
Log: PString;
begin
New(Log);
Log^ := 'Log: current stag is ' + IntToStr(I);
SendMessage(hLogWnd, WM_UPDATEDATA, 0, LPARAM(Log));
Dispose(Log);
end;
You are disposing your allocated string twice. At best, you will get an exception in your worker thread after SendMessage() has exited, terminating your thread if you do not catch that exception. At worse, you might not get an exception, but you will trash memory, leaving your app in a bad state so all sorts of random things can happen. You need to dispose the allocated string only once.
You are not responsible for removing sent messages from the queue because SendMessage() does not put the message into the queue. However, it does require the receiving thread to pump its queue for new messages, even if there are no new messages in the queue, in order to dispatch sent messages that are crossing thread boundaries, like your message is. If SendMessage() is blocking then your main thread is not pumping the queue correctly in code you have not shown, such as if you have other code that has blocked the main message loop from running.
As for the code you did show, I would suggest the following change:
procedure TForm1.LogWndProc(var Message: TMessage);
begin
if Message.Msg = WM_UPDATEDATA then
List1.Items.Add(PString(Message.LParam)^)
else
Message.Result := DefWindowProc(hLogWnd, Message.Msg, Message.WParam, Message.LParam);
end;
procedure TMyThread.SendLog(I: Integer);
var
Log: String;
begin
Log := 'Log: current stag is ' + IntToStr(I);
SendMessage(hLogWnd, WM_UPDATEDATA, 0, LPARAM(#Log));
end;
You do not need to dynamically allocate the string if you use SendMessage(), since it blocks the calling thread until the message is processed, ensuring the string remains valid. If you were using PostMessage() instead, then you would need to dynamically allocate (and fix your erroneous use of Dispose()):
procedure TForm1.LogWndProc(var Message: TMessage);
var
S: PString;
begin
if Message.Msg = WM_UPDATEDATA then
begin
S := PString(msg.LParam);
try
List1.Items.Add(S^);
finally
Dispose(S);
end;
end else
Message.Result := DefWindowProc(hLogWnd, Message.Msg, Message.WParam, Message.LParam);
end;
procedure TMyThread.SendLog(I: Integer);
var
Log: PString;
begin
New(Log);
Log^ := 'Log: current stag is ' + IntToStr(I);
if not PostMessage(hLogWnd, WM_UPDATEDATA, 0, LPARAM(Log)) then
Dispose(Log);
end;
I wrote a function to clear the keyboard buffer, since I think I am having leftover input after a get, but it's just taking input forever.
here is the get(String(1..10)
--getString10--
procedure getString10(s : in out string10) is
Last: Integer;
begin
s := (others => ' ');
Get_Line(S, Last);
end getString10;
and here is the flush I made, pretty much copied from a wiki on clearing keyboard buffers
--flush--
procedure flush is
char : character;
more : boolean;
begin
loop
get_immediate(char, more);
exit when not more;
end loop;
end flush;
Whenever the flush is called, whatever I type is outputted on the screen until I exit the program.
Also, my getString10 function doesn't always wait on user input. For example, if I have
put("Enter a name: ");
getString10(name);
put("Enter a number: ");
getString10(number);
The output will be
Enter a name: Enter a number: exampleinput
I am using Ada 2005 on Gnat Programming Studio.
Updated with the entire main:
with Ada.Text_IO, Ada.Integer_Text_IO, BinarySearchTree;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure lab4 is
subtype String10 is String(1..10);
-- function "<"--
function "<"(TheKey: in String10; ARecord: in String10) return Boolean is
begin
for i in integer range 1..10 loop
if TheKey(i) <= ARecord(i) then
return true;
else
return false;
end if;
end loop;
return false;
end "<";
-- function ">"--
function ">"(TheKey: in String10; ARecord: in String10) return Boolean is
begin
for i in integer range 1..10 loop
if TheKey(i) >= ARecord(i) then
return true;
else
return false;
end if;
end loop;
return false;
end ">";
-- function "="--
function "="(TheKey: in String10; ARecord: in String10) return Boolean is
begin
for i in integer range 1..10 loop
if TheKey(i) /= ARecord(i) then
return false;
end if;
end loop;
return true;
end "=";
--getString10--
procedure getString10(s : in out string10) is
Last: Integer;
begin
s := (others => ' ');
Get_Line(S, Last);
end getString10;
--flush--
procedure flush is
char : character;
more : boolean;
begin
loop
get_immediate(char, more);
exit when not more;
end loop;
end flush;
package BST is new BinarySearchTree(String10, String10, "<", ">", "=");
Root, found : BST.BinarySearchTreePoint;
choice : integer;
nameTemp, phoneTemp : String10;
begin
BST.setRoot(Root);
loop
new_line;
put_line("Options:");
put_line("1 - Insert a record");
put_line("2 - Find a person iteratively and print their phone number");
put_line("3 - Find a person recursively and print their phone number");
put_line("4 - Traverse the tree from a person to a person");
put_line("0 - Quit program");
put("Choose an option: ");
get(choice); put(choice, 0); new_line;
case choice is
--case 1
when 1 =>
put("Enter the name: ");
get(nameTemp); put(nameTemp); new_line;
put("Enter the phone number : ");
get(phoneTemp); put(phoneTemp); new_line;
BST.InsertBinarySearchTree(root, nameTemp, phoneTemp);
--case 2
when 2 =>
put("Enter the name of the person to find: ");
get(nameTemp); put(nameTemp);
BST.FindCustomerIterative(root, nameTemp, found);
if BST.isNull(found) then
new_line;
put("Customer not found!");
else
new_line;
put("The phone number is: ");
put(BST.CustomerPhone(found));
end if;
--case 3
when 3 =>
put("Enter the name of the person to find: ");
get(nameTemp); put(nameTemp);
BST.FindCustomerRecursive(root, nameTemp, found);
if BST.isNull(found) then
new_line;
put_line("Customer not found!");
else
new_line;
put("The phone number is: ");
put(BST.CustomerPhone(found));
end if;
new_line;
--case 4
when 4 =>
put("Enter of the name of the person to start traversal at: ");
get(nameTemp); put(nameTemp);
BST.FindCustomerRecursive(root, nameTemp, found);
put("Enter then name of the person to stop traversal at: ");
get(phoneTemp); put(phoneTemp); --using phoneTemp for a name here
BST.FindCustomerRecursive(Root, nameTemp, found);
while BST.isNull(found) /= true loop
put_line("Name = " & BST.CustomerName(found));
BST.setNode(found, BST.InOrderSuccessor(found));
end loop;
--case 0
when 0 =>
exit;
--others
when others =>
put_line("Invalid choice!"); new_line;
end case;
end loop;
end lab4;
I switched out all of the getString10()s with get()s because I am trying to debug the threaded binary search tree. I am using an input file, so it's fine for now, I just can figure out why the other methods wouldn't work.
All of the get calls on nameTemp and phoneTemp should be getString10() calls.
You could use the get_line() function, which automatically clears the keyboard buffer.
with ADA.TEXT_IO; use ADA.TEXT_IO;
with ADA.TEXT_IO.UNBOUNDED_IO; use ADA.TEXT_IO.UNBOUNDED_IO;
with ADA.STRINGS.UNBOUNDED; use ADA.STRINGS.UNBOUNDED;
procedure MAIN is
type STRING_10 is new STRING(1..10);
procedure GET_STRING_10(S : in out STRING_10) is
BUF : UNBOUNDED_STRING;
begin
BUF := GET_LINE;
for I in STRING_10'range loop
if I <= LENGTH(BUF) then
S(I) := ELEMENT(BUF, I);
else
S(I) := ' ';
end if;
end loop;
end GET_STRING_10;
S : STRING_10;
begin
GET_STRING_10(S);
PUT_LINE(STRING(S));
end MAIN;
EDIT after reading the whole main:
You should insert a SKIP_LINE; after GET(CHOICE);. Then you can replace every GET by a GETSTRING10 in your different cases.
In general, a get must always be followed by a skip_line. There's no need to do this with a get_line, thus you don't have to modify you getstring10 procedure.