|
Since you are dealing with machines located all over the world the only constant would be the server, correct? In which case I would consider having it send the closeout signal rather than worry about coordianting thousands of machines; 24 hours from now is still 24 hours regardless of where you are in the world. It would certainly make it easier to update the schedules when necessary also. I know the language. I've read a book. - _Madmatt
|
|
|
|
|
The remote machines, the "clients", don't need to do anything when their "closeout" time is reached. The host process, the "server", needs to do some stuff and then send a message to the client. And the closeout times can be unique for every client (inasmuch as the number of seconds in a day permit).
|
|
|
|
|
Ok. When the client initial connects have it send a refresh interval that is stored at the server, like in a hashtable as you mention. I know the language. I've read a book. - _Madmatt
|
|
|
|
|
Maybe you could have a table/list of all of the client's update times which is sorted in an acending manor. Then you have a single, one-time use timer that fires when the next client needs to be updated. The first thing you do in your timer callback is create a new timer that fires at the time needed for the next client in the table/list?
|
|
|
|
|
Thanks for your reply. That is along the lines of what I meant by using a hashtable in my original post. Glad to see it wasn't a far-fetched idea.
|
|
|
|
|
Anticast wrote: create a new timer that fires at the time needed for the next client in the table/list?
and what happens when the times are the same for multiple clients or close enough that the timer can't start and trigger in time? A situation that is very likely to occur with thousands of clients involved. I know the language. I've read a book. - _Madmatt
|
|
|
|
|
I'll implement a stack or queue and add all clients to it when it is determined that their closeouts need to be processed. Processing will be done in a separate thread and locks will be used to control access to the stack/queue/whatever.
|
|
|
|
|
Mark Nischalke wrote: what happens when the times are the same for multiple clients
I would be using a linked list kind of format and at the start of the Timer callback set the new Timer to fire at _nextClient.ClientPingTime, and ...
Mark Nischalke wrote: close enough that the timer can't start and trigger in time
... if its in the future, great, if its already passed then the callback should be executed almost immedatley. If he needs more precision than that he probably shouldn't be using timers anyway.
|
|
|
|
|
Interesting puzzle... Ok, how about this...
A) Dictionary/HashTable linking client ID to a record that includes ID and next scheduled time.
B) Doubly-Linked List, arranged by time, of the same records
C) Variable (Let's call it the "Insert Point") that stores either the last element of the list (If it's too short) or a reference to the entry nearest to 24 hours from now.
So it would work like this:
1) New client connects... Check the dictionary to see if he's already scheduled (If so, nothing needs to be done). If not, go to the Insert Point, which should be set to a record -about- 24 hours from now. Shift it forward if needed, insert a new record into the Linked List with that client's scheduled time, and put the record in the hash table too.
The Linked List is used so we can efficiently insert a new item at any point, without having to reallocate the underlying array (A List in .NET is just a dynamic array).
The Insert Point is needed because it would take O(n) time to iterate through the list to find the right location. The main disadvantage to using a Linked List.
2) Instead of a timer, work directly with a continuous background thread... It runs in an endless loop... Look at the Linked List, see if the first element's scheduled time has come... If so, process it, remove it, and tack it onto the end of the list. If not, Sleep() the thread until the next one is due.
This isn't a perfect solution, but it might give you a starting point... There's one main issue with this approach. There won't be even distribution...
With this simplified process, they would be allocated as they first connected... If you find a way to distribute them more evenly, you'll need a more efficient insertion mechanism. One that comes to mind would be a 96-member sorted dictionary, with its members pointing to locations in the list that are approximately 15 minutes apart (24 * 4). You could use that to estimate the proper location, then search the list itself to get the exact spot. If you use a method like this, you'll also need to give the background thread a maximum sleep time (Don't want it sleeping for an hour, when you just inserted a new record for 2 minutes from now).
|
|
|
|
|
Server maintains a table, if a client is connected and the current time is past the due time for the message ask the client for it. Client of course waits for this message. Then when a client connects from an outage the message will still be sent as appropriate. Or if the client is always on the server requests it from the client. This would allow the message to be deffered during a period of extreme load.
|
|
|
|
|
Hi,
Maybe I still did not fully understand it, however I see no major problem.
I assume the server has all necessary data available, so it can compute when it decides to do so; the client involvement is limited to receiving a "done" signal.
Here is my attempt to keep it simple:
1. the server maintains a database table "tasks" with fields "clientID", "startTime", "state", and more if necessary.
2. the server has one or a few threads that:
- execute a transaction containing a task fetch ("SELECT TOP 1 FROM tasks WHERE state='idle' ORDER BY startTime" ) and an assignment setting state to "assigned".
- wait until the start time is reached;
- execute the task;
- update the state to "done"
3. the client has a thread polling the same database (say once a minute), looking for its task with a "done" state.
Pros:
1. Simple.
Cons:
1. An urgent task added at the last moment, will not execute right away, as each server thread is either busy, or has already decided which task to do next. So the latency is one full task.
2. The client isn't event driven, as servers are not supposed to send something to their clients without an explicit request.
|
|
|
|
|
Sounds like you have two questions here.
- How do you process the data on the server at the correct time?
- How do you notify the client of the finished processing?
For #1, I'd say you can do as another person said and store the times in a database table with an index on the date to process. That way, the lookup is very quick. You could use SQL Server or SQL Server CE for this simple task. If you want it to be in-memory, you could store it in a binary tree or some other sorted structure. If the list is not modified very often (sounds like it isn't), the recommendation somebody else had of using a linked list would be a good one. You just get the next time that will be processed and start a timer that will go off at that time (or you have a background thread and use sleep commands to do the same thing). You then either maintain a pointer (linked list) to the next item to be processed or use the date (binary tree, indexed database table) to find the next item to schedule.
For #2, that depends on if the server can send notifications to the clients. If not, then I'm sure the clients know when they are expecting the data to be processed (if not, they can ask the server when they're supposed to be processed). You can have the clients request, from the server, the data at that time of the day... you can do this using a timer. If the data hasn't been generated yet, you can either have the server block until it is generated, then return the data back to the client. Or, you can have the server send a response saying "sorry, no data yet". The client can wait a second, then send the request again. Then 2 seconds, then 4, then 8, then 16, and so on (doubles each time) until it finally gets a valid response.
One thing you'll need to consider is where you will store the data until the client requests it. You could store that in a dictionary in memory, or you could serialize it and store it to a database until the client requests it.
|
|
|
|
|
No, I am only looking for suggestions toanswer your #1. I know how I will code for your #2. Thanks for the suggestion.
|
|
|
|
|
This is similar to how I've done scheduled reporting and such. As others have said, I'd use a database table with the clients and their due times. A Windows Service periodically queries the table for any jobs past due, performs them, and updates the next run time.
|
|
|
|
|
Is there a way to save an object to my exe rather than a different file? The MSDN talks about user settings, but it says that they're saved to a user.config file, and it says that application settings can only be modified at design-time.
I'm looking for a way to move a dynamic list around to different computers while only needing to move one file (the exe).
Any ideas?
Thanks!
|
|
|
|
|
Use a database I know the language. I've read a book. - _Madmatt
|
|
|
|
|
Anticast wrote: The MSDN talks about user settings, but it says that they're saved to a user.config file, and it says that application settings can only be modified at design-time.
If they(MSDN) say you can only do it at design time they are wright. Here's why. Because altering the
data in a *.exe is dangerous. It is a compiled file so you can only add stuff at compile/design time.
So no I don't think there is.
As for a workaround I would create a binary file and store the byte[] for the object be it picture or whaterver in there. Or in a light weight DB such as SQLite or even a *.mdb file and store it in a byte or OLE Object for the *.mdb.
Just an ideea. Sure others have better ones. You just have to keep looking.
|
|
|
|
|
There is a way, it is complex and I haven't done so yet. Here is the gist of it:
- (first) exe holds executable code for second exe as a resource;
- first exe creates said second exe, launches it and exits;
- second exe modifies first exe, then launches it;
- first exe deletes second exe.
I don't think anything is worth all this trouble.
|
|
|
|
|
wouldn't this be a chain reaction?
|
|
|
|
|
if need be, use the control rods, aka Task Manager.
|
|
|
|
|
The short version is no, you can't.
The long version is yes, but only if you know EXACTLY what you are doing, the structure of an .EXE, how to update the various tables with new offsets, yada, yada, yada, ... and hope while you're doing this, that your app doesn't crash or the system doesn't hang, or the power doesn't fail, in the middle of writing the data to the .EXE file.
|
|
|
|
|
Okay, thanks everyone for your input!
|
|
|
|
|
You could save it in a resource file. The requirement is kinda strange, though..45 ACP - because shooting twice is just silly ----- "Why don't you tie a kerosene-soaked rag around your ankles so the ants won't climb up and eat your candy ass..." - Dale Earnhardt, 1997 ----- "The staggering layers of obscenity in your statement make it a work of art on so many levels." - J. Jystad, 2001
|
|
|
|
|
You have two options. I'll describe them in different posts so you don't get confused. This first post will be for the simpler option.
First, add a resource (say, a string) to your application like your normall would, at design time. Only, make sure the resource is as large as the largest list you expect to embed in the EXE. Make sure the beginning and end of the string has unique data that is not likely to appear randomly in the rest of the EXE. When you compile it, open the EXE in a hex editor and find the unique parts of the string. Make note of the beginning and end of the string in the EXE.
Modify the string in the EXE (using the hex editor) so that the first part of the string that is not being used up by the unique string at the beginning of the string contains a number: the index into the EXE the string is embedded. That way, at runtime, you can parse the string and find out where it is located in the EXE. You can then have the EXE copy itself to a new file and have it modify the new EXE to contain the new data in the string resource (after the number that indicates the byte position of the string in the EXE). You then have the old EXE run the new EXE and close itself... the new EXE will then presumably clean up the old EXE by deleting it. If you want, the new EXE could then copy itself to where the old EXE used to be and then run the newer EXE, which will then delete the new EXE. You can use a counter parameter to keep track of how many EXE's you are into the process.
Also, since the resource string is a constant length, but the data it contains is variable length, you'll either want to delimit the end of the actual resource data, or create another number in the beginning of the resource string that indicates the length of the data.
|
|
|
|
|
Now that you know the simple way, I'll describe the harder way.
First, you use the ResourceWriter class to create a resource out of the data you want to be embedded in the EXE.
Next, you use the C# compiler to embed that resource file into an assembly (the assembly is created from some C# code, so you can't just embed it in your primary assembly straight away). What is in the assembly is unimportant, as it will be ignored (you only want the assembly so you can have an assembly with a resource file embedded in it).
Next, you can use ILMerge to combine the assembly you just created with your main assembly.
You can then load that resource at runtime.
Those are most of the tools you'll need to accomplish this task. However, there are a few points you'll need to consider.
For one, you don't want multiple EXE's, so you'll want to make sure to embed the above tools into your main assembly (CSC and ILMerge). To run them, you'd then extract them to a file, run them, then delete them when you are done running them.
Next, combining the generated assembly with your main assembly will increase the size of the main assembly. To prevent it from increasing in size each time you run it, you'll wan to first compile the assembly and then add the assembly as a resource for itself. When you merge the main assembly with the generated one, you'll actually add the main assembly to the resource file you generate, then compile that resource file into a new assembly, then merge that generated assembly with the assembly you export from your resources.
The above ResourceWriter only works with strings. So, when you want to embed EXE's (aka byte arrays) and images and such, you'll need to embed them as strings. That means you'll have to do some parsing and deserializing.
You will have to do some switching of EXE's (i.e., delete the old one and run the new one) as I described in the "simple way" in my previous post.
|
|
|
|
|