Threads are ways to run multiple things in parallel, at the same time. Each program in Oxygene has 1 or more threads, by default just one, which is the main thread, which is the one in which the gui code runs. Using multi-threading allows you to run something in the background, while keeping your main thread responding to actions from the user (in a gui application). It’s also useful when dealing with blocking operations, like sockets and data access. There’s no practical limit to the number of threads that run at once, it depends on the system resources (memory, cpu) how many will work at once.
The thread class represents the basis for all the other ways of working with threads. The thread API is simple to use. The constructor takes a delegate to run as the method for a thread, the “Start” method makes the thread start for the first time. “Join” waits for the thread to finish.
|var lThread :=new Thread(methodbeginvar lClient := HttpClient;tryvar lResponse := lClient.Get('http://blogs.remobjects.com/feed/rss').Content.ReadAsString; Invoke(methodbegin// synchronize to the main thread LoadResponseData(lResponse);end);excepton e: Exception do Invoke(methodbegin MessageBox.Show('Failed: '+e);end);end); lThread.Start;|
The ThreadPool class offers static methods to run something when a CPU is available. It will create up to a certain number of threads (configurable, defaults to the number of CPU cores) and execute any task it gets given to it in order. Using the Thread Pool is simple. The QueueUserWorkItem method takes a delegate with an optional “state” arguments that gets passed to this delegate.
|method MainForm.UpdateRssFeed;begin ThreadPool.QueueUserWorkItem(methodbeginvar lClient := HttpClient;tryvar lResponse := lClient.Get('http://blogs.remobjects.com/feed/rss').Content.ReadAsString; Invoke(methodbegin// synchronize to the main thread LoadResponseData(lResponse);end);excepton e: Exception do Invoke(methodbegin MessageBox.Show('Failed: '+e);end);end;end);end;|
The Task class was introduced with .NET 4.0 and introduces a new way to deal with threading. A task is an asynchronous operation. There are two classes, the Task class has no return value, while the Task
|var lTask :=new Task|
|var lTimer :=new Timer(methodbegin Console.WriteLine('Timeout!');end,nil,5000,250);|
|var lAsync :=newasync lClient.Get('http://blogs.remobjects.com/feed/rss').Content.ReadAsString; ... MessageBox.Show(lAsync);|
When to use what
While opinions may vary on the subject, I tend to use the System.Threading.Timer for anything that runs multiple times at a constant interval. The futures map to Task or the thread pool depending on the .NET version, which I would use over those two. If for some reason I would not want to use a future, then I’d use the Task class when I’m developing against .NET 4 or above and have a short running thing to do, the Threadpool for short running things on versions that lack the Task class, and Thread for anything else.
When running in a thread, sometimes you have to tell the GUI that data is available. Updating the GUI from the thread is not safe, and usually leads to hard to find errors. The way to deal with this is using Windows Forms “Control.(Begin)Invoke”, or WPF/Silverlights’ “Dispatcher.(Begin)Invoke”. These apis take a delegate and will execute this delegate on the main thread. The Invoke methods will wait for the main thread to be done with the given task, the BeginInvoke methods will not and let the thread continue right away.
Accessing a complex object is not generally a safe operation. Most objects in .NET are not thread safe, and will fail with strange errors when accessed from multiple threads at once. Do deal with this, you can use locking or interlocked access to simple fields.
Locking can be done with the “locking” statement. Locking is done on any object instance, generally this is an object not used for anything else.
|type MySafeStringList =classprivate fLock: Object :=new Object; fList: List|
|CheckForUpdatesThread =classprivate fEvent: ManualResetEvent :=new ManualResetEvent(false); fThread: Thread; method Work;publicconstructor;method Stop;method CheckForUpdates;end; method CheckForUpdatesThread.CheckForUpdates;begin...end; method CheckForUpdatesThread.Work;beginloopbeginif fEvent.WaitOne(10000)then// Wait for 10 seconds, returns true if it was set.exit; CheckForUpdates;end;end; method CheckForUpdatesThread.Stop;begin fEvent.Set; fThread.Join;// wait for it to finishend; constructor CheckForUpdatesThread;begin fThread :=new Thread(@Work); fThread.Start;end;|
.NET 4.0 introduces several new collections that are safe access from multiple threads. Among these structures are ConcurrentDictionary and ConcurrentStack. These have slightly different methods for adding and retrieving members than their non-thread safe counterparts but provide a fast way to work concurrently with shared data.