Showing posts with label threads. Show all posts
Showing posts with label threads. Show all posts

Tuesday, 29 September 2009

Many threads make light work



Today is the final episode in my threading story. Next time, I'll talk about bread, and possibly Wicken Fen as well. The image on the left is of my coffee cup in front of its cafetière. The strange felt object is a coffee cosy, kindly made by my sister. Bet you don't have one of those!

Previous episode

As I hinted before, the separation of incoming stream from the list of words created, nice though it is as far as Object Orientation is concerned, makes even more sense if you are building up words from several streams and adding them to the same wordlist. Like trying to find out how many times the word "thriller" came up just after Michael Jackson's death (I'm trying to think of a one-word example!).

So, this time we'll start with an array of readers. This one has three. I amended the assignReaderContent function to give each one two different paragraphs from The Star by Arthur C Clarke (the first 6, in fact). I then created a single WordList and a list of WordListBuilder and a list of Threads. All Threads (there will be three) must share the same WordList. The function setupBuilderList is passed all three lists plus the WordList object, and associates them all together:

    static void setupBuilderList(List<WordListBuilder> bList,ICharReader[] rArray, List<Thread>threadList, WordList wList)
    {
        for (int i = 0; i < rArray.Length; i++)
        {
            bList.Add(new WordListBuilder(rArray[i], wList));
            WordListBuilder.buildWordListStarterOp listStarter = bList[i].buildWordListStarter;
            threadList.Add(new Thread(new ParameterizedThreadStart(listStarter)));
        }
    }
There is a lot going on here, and quite a few new parts of ASP.NET and the classes I have created to explain. Of course, at this point there are no Threads or WordListBuilders, so we have to make one builder to enclose each reader and associate each with a thread. We create a new builder for each reader, as before, passing the reader and the wordlist to the constructor. Then we declare a delegate, called buildWordListStarterOp for the builder in question to communicate with the rest of the operation.The starter op. in question is a member function called buildWordListStarter, which has to be passed an object (as does the delegate):

        public void buildWordListStarter(object state)
        {
            if (state is BuildWordListParams)
            {
                BuildWordListParams oP = (BuildWordListParams)state;
                oP.myWordListBuilder.buildWordList();
            }
        }

        public delegate void buildWordListStarterOp(object state);

In the setup function, a ParameterizedThreadStart object is required, so we can give the thread something to work with. The parameter has to be of type system.object. the BuildWordListStarter member function expects to be handed an object of the class BuildWordListParams:

class BuildWordListParams
    {
        public WordListBuilder myWordListBuilder;
        public BuildWordListParams(WordListBuilder a)
        {
            myWordListBuilder = a;
        }
    }

Of course, since we are only passing a list, and a list is already a system.object, it is not necessary to go to the trouble of declaring BuildWordListParams. On the other hand, it is now there, and if I wanted to pass something else, for example and instruction to ignore common words such as "the" and "it", I can just add them to this declaration and it remains just one object.

This class simply passes on an object of type WordListBuilder. Assuming all goes well (and I confess, we don't have any exception handling here) this means you can take the BuildWordListParams object and just call its buildWordList member function, the same one we have been using all along.

In the static void function, this buildWordListStarterOp is called listStarter and is then passed to the new Thread in its constructor. It is perhaps not necessary to delve into the workings of threads in general. I take the view that the ASP.NET team write Thread libraries so I don't have to.

We have a list of builders and a list of thread. We now set up the timer with just the same function as last time.

The last static function (startThreads) is to start each thread off, giving it the correct parameter as it goes:

    static void startThreads(List<thread> tList, List<wordlistbuilder> bList)
    {
        int i = 0;
        foreach (Thread t in tList)
        {
            BuildWordListParams p = new BuildWordListParams(bList[i]);
            t.Start(p);
            i++;
        }
    }

Each Thread is passed the corresponding WordListBuilder.

One more, rather important, point. If you have three threads able to access a resource, such as a list of words at the same time, you may well end up with rubbish results. The content of both member functions of WordList, AddNewWord and makeReportCalledBack are enclosed in a lock statement. This ensures only one thread (we hope, containing a builder) will access it at any one time. It will then be released for another thread to use.

Here are some screenshots:

Here, you can see that the main thread is number 9, and that numbers 10-12 have been assigned to the different builders.

Here, the main thread has finished, all builders have been assigned their char readers and all threads have been started.


We are getting the first report from thread 6, which is the Timer/WordList.













At this point most or all of the content has been read. I counted up instances of the word "that" in my 6 paragraphs and there are, indeed 10.

Here, two of the threads have hit the EndOfStreamException  and have been closed.
What I do find odd is that all reports cease at this point. The previous version kept making the same report over and over again. I do not understand this, and suspect I have introduced a bug.

AT some point I will try to get all of my files up here in a zip, so you can have them, if you want.

Friday, 25 September 2009

Timing and threads

Jackies carrots
Yesterday was market day and I forgot to take my camera with me. Sorry. But I will include an image from our market that, like Biddy Baxter, I took earlier. This one is from Jackie's famous fruit and veg stall. She's a real quick thinker, so no dithering!

Previous Episode

I said I'd get on to processing inputs from different sources. This is two problems really: one is, there might well be delays in transmission. In this case, it would be better to make regular reports (every 8 secs?) so you don't think your computer has died. You need a timer for this. The other is that your various sources have to be able to add words and counts to the same list without trading on each other and making a mess of the count. This suggests threading to me. Luckily, both timers and threads come from the same library in C#, as they've already forseen you might need both for some tasks. Whatever language you use, you will most likely need a special library for thread, because they go deeper than I want to into the workings of the machine in question. I am NOT a back-end developer (oooh missus!).

Thing is, I had to learn this as I went along, as I haven't even thought about simultaneous processes since Uni (and I'm too old to use that abbreviation, besides it was a Poly then). I turned to the one of my C# books I have only recently started reading again: Pro C# with .NET 3.0 by Andrew Troelsen. I usually refer to the more webby Pro ASP.NET 3.5 in C# 2008 by Matthew McDonald and Mario Szpuszta. Both are Apress books.

So, I now have a slightly different char reader called SlowedDownCharReader. This one uses a private random number to produce slight delays in transmission, which is perhaps a bit more like real life:

      Thread.Sleep(this._random.Next(200));

happens at the beginning of the fetchNextChar method. The random number returned is multiplied by 200 to give some hundredths of a second. This soon mounts up when you only get one char at a time! Note that the word Thread is already coming up. Random delays and timers are all things that affect the current thread of execution. Using these classes requires a using of System.Threading at the head of all relevant files.

Back in the main function, I've written a static setupTimer function. This is where it gets tricky. Since the operation we want to happen every 8 seconds is the WordList method makeReport, we need to create a C# delegate for the Timer to call. This has to be a void function passed a System.Object parameter:

    public delegate void makeReportOp(object state);

Here is the whole setupTimer function:

    static void setupTimer(WordList list, int secsToStart,int intervalSecs)
    {
        int millisecsToStart = secsToStart * 1000;
        int millisecsInterval = intervalSecs * 1000;
        WordList.makeReportOp reporter = new WordList.makeReportOp(list.makeReportCalledBack);
        TimerCallback timeCB = new TimerCallback(reporter);
        Timer everySoOften = new Timer(timeCB, null, millisecsToStart, millisecsInterval);
    }

First of all, the time before the Timer is called and the interval time are passed as seconds and then multiplied by 1000. No one thinks in milliseconds! Now the delegate is created and passed the WordList method to call. I have created a new report method, which sorts the list and writes it to console just as before but writes a short message before and after, so you can see what is going on. The method is locked, so that only one thread can access it (we are envisaging only one wordlist at a time). This message contains the thread number as well, because it is different from the main thread of the program. A TimerCallback delegate is created and passed the reporter delegate. Once that is done, all that is needed is to create a Timer object and pass it the callback, so it knows what to do. It is the Timer which works in its own thread of execution.

This all leads to a strange effect. Every 8 seconds (the interval I chose) a report is made to the console. These get progressively longer as more words get added. At some point, the fetchNextChar will generate its exception, print out a message to the console and stop the main thread. Nothing stops the timer except switching off the console window, so it will carry on printing the same list. I'm told killing threads is a difficult business. But this test case was imagining a continuous process of reporting on different streams coming in, so we won't worry about that.

Next time we'll set up 3-4 threads all putting their words into the same list.






Next Episode