 |
|
 |
this article covers what i'm looking for but i'm at a lost on how it works. i'm trying to understand what is actually updating the progressbar.
i need to call 3 progressbars in my c# project. i did not build a "dll" for the threading. my application calls multiple functions in a linear fashion. i want to give a status to specific progressbars as the function is called and processes it request.
my head is spinning with this threading (calling the backgroundworker).
in your project you call the "OnJobEvent(new JobEventEventArgs(ThreadName, "Progress: " + i.ToString()));" to call your thread class. can you help me understand how it is possible to mimic what you are doing?
//sample of code...
private void ListLDN_Start(string[] filenames, BackgroundWorker
worker, DoWorkEventArgs e)
{
//starting conerting the listldn into a comma delimited file
:
:
//start the body of the work
lcount = build_master_gspfile(filenames);
build_master_gsparray(lcount, worker, e);
compare_files(lcount, filenames);
//finish the body of the work
}
private int build_master_gspfile(string[] filenames)
{
int lcount = 0; // file list incrementer to walk through the
file list
string data = null; //ra dta string read from input file then
stored in ouput file for later
string temp1 = null, temp2 = string.Empty, site_number = null;
int comma = 0, quote = 0, a = 0;
bool peg = false; //use this to gauge if a record was matched
lcount = 0;
//master file stored in program dir.
StreamWriter sw = new StreamWriter(master_file);
try
{
for (lcount = 0; lcount < filenames.Length; lcount++)
{
:
:
worker.ReportProgress(iSite / 16);
:
:
}//while (sinput.Peek() >= 0)
//should peg not set to turn there was an error in the
gsp file with
//its own site number
if (peg == false)
{
MessageBox.Show("Process stopped because local site
number (" + temp1 +
") is missing in its own file: " +
filenames[lcount] +
". Program error, system Abort.");
Application.Exit();
}
}//for(lcount = 0; lcount < filenames.Length; lcount++)
}
catch (Exception err)
{
ErrorFile ef = new ErrorFile();
:
:
}
sw.Close();
return lcount;
}//private void build_master_gspfile()
private void build_master_gsparray(int lcount, BackgroundWorker
worker, DoWorkEventArgs e)
{
//open the gsp master file and load the master gsp array
int iSite = 0, iElements = 0, iTGSP = 0, iComma = 0;
StreamReader mgsp = new StreamReader(master_file);
mgsp_count = 0; //initialize the elemenst count of number of
gsps stored in
//master gsp array.
//the mater_file is comma delimited and quote defined text format.
//need to read each line and extract the comma elements and
populate
//the master_array(#sites, #elements).
while (mgsp.Peek() >= 0)
{
:
:
if (iElements == 98 && rBngsp.Checked == true)
{
iTGSP = Convert.ToInt16(master_gsp[iSite, 1]);
tgsp_array[iTGSP] = "1";
worker.ReportProgress(iSite / 16);
iSite++;
i = 0;
iElements = 0;
iPlace = 0;
}
if (iElements >= 96 && rBListLDN.Checked == true)
{
if (iElements == 96)
{
master_gsp[iSite, 96] = "";
master_gsp[iSite, 97] = "";
}
if (iElements == 97)
master_gsp[iSite, 97] = "";
iTGSP = Convert.ToInt16(master_gsp[iSite, 1]);
tgsp_array[iTGSP] = "1";
worker.ReportProgress(iSite / 16);
iSite++;
i = 0;
iElements = 0;
iPlace = 0;
}
}//for (int i = 0; iSite < lcount; ++i)
if (iSite == lcount)
break;
}//while (mgsp.Peek() >= 0)
mgsp.Close();
mgsp_count = iSite;
}//private void build_master_gsparray(int lcount)
|
|
|
|
 |
|
 |
This is good. I would recommend refactoring the thread-management portion from the UI into its own service with some kind of abstract interface.
There are two benefits to this:
1. The UI object becoms much simpler, it only really needs progress and error information.
2. People who already have a job manager can use their UI simply by providing an Adapter[^] compliant with the interface required by your UI component.
|
|
|
|
 |
|
 |
Max,
I have been thinking about how to make it easier to use as well as increase the functionality. I like your idea better though. I am not sure how I would implement the interface. How would you link the thread to the job manager? Would the ThreadConainer class expose a collection of Adapters?
Thanks,
Richard
|
|
|
|
 |
|
 |
R2B2 wrote: I am not sure how I would implement the interface.
I can guess. This is a product of your intent, so you will have to make the final call.
What does the Thread View really need to know? It needs to know about a single job, and that said job exposes a total amount of work to do, the current amount of work done, and a status line.
What does the Thread Container really need to know? It needs to know that there is a thing that manages jobs and it needs to be able to get a list of jobs from that thing. It also needs to know how to create Thread View objects and link them to individual jobs.
So the idea would be to create some minimal service interfaces consumed by your classes, then implement them. You might not even care to implement them. You might just mock implementations of those interfaces out for demonstration purposes and leave it as an "exercise for the reader" to implement those interfaces.
Once you have your views bound to an abstract interface, the only matter left to attend to is synchronization. I'd recommend making part of the job manager and job interfaces' contracts that they are thread safe. Then you could just set up a timer in your thread container, and poll all of the jobs at some reasonable, but non-intrusive interval (30ms, for instance).
R2B2 wrote: How would you link the thread to the job manager?
I would encapsulate the threads behind the job manager or, perhaps encapsulate each thread behind a job. Right now you appear to be queuing jobs to the system worker threads. That's fine, so long as none of the jobs block for significant periods of time. Encapsulating your asynchronization behind your job interface will allow you to (a) change how you do it easily, (b) mock it out, and (c) dynamically adjust how you do it (as in: give users the option to do it either with the system worker threads or with their own threads).
R2B2 wrote: Would the ThreadConainer class expose a collection of Adapters?
The Adapters wouldn't come into play until someone else was using your code who has already implemented his own job manager. Then he would write an Adapter for his job manager that conformed to your interface and plug it into your class.
That's a long post. Did all of it make sense?
|
|
|
|
 |
|
 |
Thanks for your responses Max,
Yes it did make sense. My intent was to create a generic control that people could drag onto their form and write a little code and put calls within that to fire the progress and the error events wherever needed. That said, it is becoming clearer that what I have created may not be the most useful thing.
I will ponder this a bit and get back to you.
Thanks,
Richard
|
|
|
|
 |
|
 |
What you've created is very useful, Richard. I'm only suggesting a way of making it more useful by delivering it in smaller pieces. You can have your cake and eat it, too. That's the beauty of software design. If you want something that is easy to use but you also want people to be able to vary how parts of it behave, you can split it into little pieces and then provide default implementations for each of the services upon which it depends.
For instance: if you define a job container view and a job manager service contract, all you have to do is make it so that the view selects a default job manager implementation in the absence of an alternative and you get the simple use characteristics you seek and the ability for people using this tool to plug in their own sets of behavior.
|
|
|
|
 |
|
 |
I do want to make it more useful. I believe I am understanding what you are saying now. I believe you are suggesting using WCF over IPC to communicate to the threads?
Thanks,
Richard
|
|
|
|
 |
|
 |
Almost: I'm saying encapsulate part of what you've done from the other part so that it would be easier to switch to WCF, or any other solution, should someone want to do so.
|
|
|
|
 |
|
 |
Aha. This is what I understand you to be saying. Pull out the thread management into a job handler and then create an interface to be able to list the jobs and their statuses. The control would scan the
I was trying to go the opposite way and have the threads inform the control when they change status. What if I take the thread management out completely and set up an IPC channel with its own contract to add/update/delete thread views, update the overall status. An additional channel could be set up to send the cancels back to the thread.
Richard
|
|
|
|
 |
|
 |
I don't really know a lot about WCF. Would people be able to swap out your thread management solution for their own using the WCF solution you suggest?
I have nothing against the threads informing the control. It may be ideal and it may not, depending on the size of the work you are doing and other factors. Control implements ISynchronizeInvoke[^]. If your controls implemented some interface that you wrote which indicates the ability to take progress updates you could easily route them through Control's BeginInvoke and EndInvoke.
For instance, if your controls all implemented an interface like this:
public interface AcceptsProgressReport
{
void UpdateProgress(int workDone, int totalWork, string statusMessage);
}
...and then your job encapsulations used an Observer pattern[^] to notify implementations of that interface when their progress has changed. Then your controls' UpdateProgress method could sync the task of actually updatin the UI to the UI thread by using Control.BeginInvoke.
|
|
|
|
 |
|
 |
What you can do with IPC (InterProcess Communication) is you can create an object in your code that is a proxy for the obect that is in my code. That proxy sends a messages via named pipes and invokes methods on my copy of the object.
I will checky into both of them and let you know.
Thanks for all your input,
Richard
|
|
|
|
 |
|
 |
That sounds pretty cool. It is one option I think people would want to employ. My preference, if I were using your library - which there's a good chance I would be if I was writing a UI - would be to have that as one kind of job that I can plug into my job manager. Ideally, I could have many different kinds of jobs - some in process, some out of process, some in a different app-domain - all managed through one manager and all presented through one slick UI component.
I look forward to hearing what you come up with.
|
|
|
|
 |
|
 |
Hi Guys!
FRom my point of view, this has been an extremely interesting discussion thread, having an (academic at this stage) interest in but very little real knowledge of multithreaded operations and their monitoring.
Can I make a completely uninformed suggestion? Would it be possible for the 2 of you to get together and create an article out of this discussion, perhaps with a more detail thrown in? I think this sort of "to and fro" weighing up the advantages and disadvantages that goes into software design is something that is often hidden from view, and leaves people wondering why choices are made by developers that don't seem to make much sense.
Any, just a thought. Please keep us informed on how this control and article progress, as the design is easily as interesting as the end-result!
Stuart R
|
|
|
|
 |
|
 |
R2B2: let me know if you are interested.
My email address is max [at] hexsw [dot] com.
|
|
|
|
 |
|
 |
That sounds good to me. I have actually been thinking of a couple of articles. I have looked at some of the technology and there are a couple of ideas that might work. Specifically using the IPC channel with remoting for same-box communication, and using WCF and MSMQ to handle distributed status/messaging and maybe even incorporating a treeview to show the status of multiple categories of things being monitored.
Max, I will give you an email shortly, but it may be a bit before the article come out
Cheers,
Richard
|
|
|
|
 |
|
 |
Thanks for the article. However, I'm having problem building the code.
I get the following error:
Properties\licenses.licx(1): error LC0003: Unable to resolve type 'DevExpress.XtraEditors.LabelControl, DevExpress.XtraEditors.v7.3
Any suggestions?
|
|
|
|
 |
|
 |
Juste remove the line in the licenses.licx from the control project.
|
|
|
|
 |
|
 |
Thanks Member 1556192. I am sorry I left the reference in there. I have updated the code in the zip file.
Richard
|
|
|
|
 |
|
 |
Hi,
I have the same problem, with this error:
Properties\licenses.licx(1, 0): error LC0003: Unable to resolve type 'DevExpress.XtraTreeList.TreeList, DevExpress.XtraTreeList.v8.2, Version=8.2.3.0, Culture=neutral, PublicKeyToken=9b171c9fd64da1d1'
not in my pc, but in the production server.
Maybe the DevExpress Suita must be installed also in this machine, not only in my pc?
Thanks in adavance.
Luigi
|
|
|
|
 |
|
 |
Luigi,
When I first created the project I used the DevExpress controls. Then I realized that would not be good to be posting an article with third party tools, so I took them out. Even though I took it out the reference to the dlls, the license still remained in the Properties\licenses.licx file. If you go to that file and delete the line from the file (I think it is the only one) and save the file it should compile fine.
Let me know if you have any other problems.
Richard
|
|
|
|
 |
|
 |
Thanks for the response Richard.
I have put the dlls on a separate folder, and the Reference of my project points to that dlls.
Now it works.
Luigi
|
|
|
|
 |
|
 |
Thanks. That did the trick.
Regards,
Steve
|
|
|
|
 |