[.net] Thread safe console output

Started by
6 comments, last by Numsgil 15 years, 1 month ago
This is what the very top level of my unit testing program looks like:

string output;
int returnValue = RunAndTimeAllTests(out output);
System.Console.WriteLine(output);
return returnValue;
Instead of outputting directly to the console everywhere in the program, I output into a string buffer, and then at the very top level output that to the console. This makes unit testing things much easier since I don't have to grab the console output or anything weird like that. Problem is that when I'm debugging, I'd like to see the console output before the entire program has managed to finish. Exploring the output variable with the debugger is counte-productive since it has all sorts of formatting the debugger ignores. So my thought was to run a thread at this top level which looks at the output buffer and asynchronously sends new input to the console in a background thread. I'm mindful of thread safety here. I don't want to have to sprinkle the rest of my code with locks. But then I realized with the interesting reference nature of strings, I can do some locking implicitly using reference assignment as an atomic operation. So this is my new code:

string output = "";
bool continueThread = true;

Thread t = new Thread(delegate()
{
	do
	{
		Thread.Sleep(0);
		string temp = output;
		output = "";
		System.Console.Write(temp);
	} while (continueThread || !string.IsNullOrEmpty(output));
});

t.Start();

int returnValue = RunAndTimeAllTests(out output);
continueThread = false;
t.Join(1000);

return returnValue;
And let's say that the code inside RunAndTimeAllTests() appends to output like this:

output += "Some message " + "Some other message " + someVariable.ToString();
Am I right in thinking this is thread safe? It gives the right output, but that's no indication. Working through it logically I'm pretty sure there aren't any cases which would result in a race condition. Certainly, since there's no locking, there's no possibility for deadlock. My main concern is the += operator I'm using throughout the code. Are there any weird edge cases where I might end up assigning output = "" in my console thread while the main thread is in the middle of a += operation? Would that screw it up at all?
[size=2]Darwinbots - [size=2]Artificial life simulation
Advertisement
Between storing 'string temp = output' and 'output = ""', a message could be written into output - and thereby be lost.
Not threadsafe.

Besides, I'd consider this sort of a deroute. Why not create an 'output'-class, and pass it to your tests?
This output-class (interface) could have various implementations. One that logs to a console, one that logs to a file etc...
| Stein Nygård - http://steinware.dk |
Hmmm... I was thinking that the assignment would be by reference, but I forgot that strings assign by value (sort of). It would work with almost any other reference type, I'm pretty sure.

Anyone have any better ideas?

Quote:
Besides, I'd consider this sort of a deroute. Why not create an 'output'-class, and pass it to your tests?
This output-class (interface) could have various implementations. One that logs to a console, one that logs to a file etc...


That's over engineering the problem.
[size=2]Darwinbots - [size=2]Artificial life simulation
Quote:Original post by Numsgil
Quote:
Besides, I'd consider this sort of a deroute. Why not create an 'output'-class, and pass it to your tests?
This output-class (interface) could have various implementations. One that logs to a console, one that logs to a file etc...


That's over engineering the problem.


I disagree.
It's done in about 2 minutes.
It adds great flexibility.
It requires no change in client code.

What's over-engineered about it?
| Stein Nygård - http://steinware.dk |
You're freely entitled to your opinion. But it smells like over engineering to me. A desired feature at the very top level of the code should almost never cause systemic changes to the lower reaches of the code if you can help it.

If you wanted a cat with 5 legs, would you genetically engineer a 5 legged cat or find an existing cat and staple a new leg onto it? There isn't really a right answer, but I'd vote for the staple.
[size=2]Darwinbots - [size=2]Artificial life simulation
Let's agree to disagree then :)
| Stein Nygård - http://steinware.dk |
I'm not sure if you realize this, but... Console.WriteLine is thread-safe by definition. It seems like perhaps the bigger problem is the logging (if you really want the string data around). If you just want to write to the screen, then you don't need any fancy logic at all.

(Although in a similar case, I use an output style class with per-thread StringBuilders and so on...)
It's not writing to the console which I find problematic. It's reading the output string as it's being written.

It's not logging necessarily. This is a framework for unit testing. So there's several hundred tests that are run, and if any of them fail, that information is presented to the user in a nice and orderly manner through the console. That is, a user using the program normally will expect to see the output string. Even if all tests pass there will be a success message in the output string that the user will want to see.
[size=2]Darwinbots - [size=2]Artificial life simulation

This topic is closed to new replies.

Advertisement