I was reading Threading from within a class with static and non-static methods and I am in a similar situation.
I have a static method that pulls data from a resource and creates some runtime objects based on the data.
static class Worker{
public static MyObject DoWork(string filename){
MyObject mo = new MyObject();
// ... does some work
return mo;
}
}
The method takes awhile (in this case it is reading 5-10mb files) and returns an object.
I want to take this method and use it in a multiple thread situation so I can read multiple files at once. Design issues / guidelines aside, how would multiple thread开发者_StackOverflow中文版s access this code?
Let's say I have something like this...
class ThreadedWorker {
public void Run() {
Thread t = new Thread(OnRun);
t.Start();
}
void OnRun() {
MyObject mo = Worker.DoWork("somefilename");
mo.WriteToConsole();
}
}
Does the static method run for each thread, allowing for parallel execution?
Yes, the method should be able to run fine in multiple threads. The only thing you should worry about is accessing the same file in multiple threads at the same time.
You should distinguish between static methods and static fields in this case. Each call to a static method will have its own "copy" of the method and its local variables. That means that in your sample, each call will operate on its own MyObject
instance, and the calls will have nothing to do with each other. This also means that there is no problem with executing them on different threads.
If the static method is written to be thread safe, then it can be called from any thread or even passed to a thread pool.
You have to keep in mind - .NET objects don't live on threads (with the exception of structs located on a thread's stack) - paths of execution do. So, if a thread can access an instance of an object it can call an instance method. Any thread can call a static method because it all needs to know about is the type of the object.
One thing you should keep in mind when executing static methods concurrently are static fields, which only exist one time. So, if the method reads and writes static fields, concurrence issues can occur.
However, there is an attribute called ThreadStaticAttribute
which says that for each thread there is a separate field. This can be helpful in some particular scenarios.
Local variables are separte for each thread, so you don't need to care about this. But be aware of external resources like files, which can be problematic when accessed concurrently.
Best Regards,
Oliver Hanappi
Aside from the code aspect, which has already been answered, you also need to consider the I/O aspect of accessing the file.
A note on architecture and how I have completed this task in the past - not suggesting that this is the one right approach or that it is necessarily appropriate for your application. However, I thought my notes might be helpful for your thought process:
Set up a ManualResetEvent field, call it ActivateReader or something similar, this will become more obvious further on. Initialize it as false.
Set up a boolean field, call it TerminateReaderThread. Initialize it as false, again this will become more obvious further on.
Set up a Queue<string> field, call it Files and initialize it.
My main application thread checks to see if there's a lock on the files queue before writing each of the relevant file paths into it. Once the file's been written, the reset event is tripped indicating to the queue reader thread that there are unread files in the queue.
I then set up a thread to act as a queue reader. This thread waits for the ManualResetEvent to be tripped using the WaitAny() method - this is a blocking method that unblocks once the ManualResetEvent is tripped. Once it is tripped, the thread checks to see if a thread shutdown has been initiated [by checking the TerminateReaderThread field]. If a shutdown has been initiated, the thread shuts down gracefully, otherwise it reads the next item from the queue and spawns a worker thread to process the file. I then lock the queue before checking to see if there's any items left. If no items are left, I reset the ManualResetEvent which will pause our thread on the next go-around. I then unlock the queue so the main thread can continue writing to it.
Each instance of the worker thread attempts to gain an exclusive lock on the file it was initiated with until some timeout elapses, if the lock is successful, it processes the file, if it's unsuccessful, it either retries as necessary, throws an exception and terminates itself. In the event of an exception, the thread can add the file to the end of the queue so another thread can pick it up again at a later point. Be aware that if you do this, then you need to consider the endless loop an I/O read issue could cause. In such an event a dictionary of failed files with counters of how many times they've failed could be useful so that if some limit was reached you could cease to re-add the file to the end of the queue.
Once my application decides the reader thread is no longer needed, it sets the TerminateReaderThread field to true. Next time the reader thread cycles to the start of its process, its shutdown process will be activated.
The static method will run on the thread that you call it from. As long as your function is re-entrant, meaning execution can safely re-enter the function while execution from another thread (or further up the stack) is already in the function.
Since your function is static, you can't access member variables, which would be one way of making it not re-entrant. If you had a static local variable that maintained state, that would be another way of making it not re-entrant.
Each time you enter you create a new MyObject, so each bit of execution flow is dealing with it's own MyObject instance, which is good. It means they won't be trying to access the same object at the same time (which would lead to race-conditions).
The only thing you're sharing between multiple calls is the Console itself. If you call it on multiple threads, they'll output over each other to the console. And you could potentially act on the same file (in your example the filename is hard-coded), but you'd probably be acting on multiple files. Successive threads would probably fail to open the file if previous ones have it open.
精彩评论