0

here's my question:

Say I have this program (I'll try to semplify as much as I can): receiveResultThread waits for result from differents network clients, while displayResultToUIThread updates the UI with all the results received.

class Program
{
    private static Tests TestHolder;

    static void Main(string[] args)
    {
        TestHolder = new Tests();

        Thread receiveResultsThread = new Thread(ReceiveResult);
        receiveResultsThread.Start();

        Thread displayResultToUIThread = new Thread(DisplayResults);
        displayResultToUIThread.Start();

        Console.ReadKey();
    }

    public static void ReceiveResult()
    {
        while (true)
        {
            if (IsNewTestResultReceivedFromNetwork())
            {
                lock (Tests.testLock)
                    TestHolder.ExecutedTests.Add(new Test { Result = "OK" });
            }

            Thread.Sleep(200);
        }
    }

    private static void DisplayResults(object obj)
    {
        while (true)
        {
            lock (Tests.testLock)
            { 
                DisplayAllResultInUIGrid(TestHolder.ExecutedTests);
            }

            Thread.Sleep(200);
        }
    }
}

class Test
{
    public string Result { get; set; }
}

class Tests
{
    public static readonly object testLock = new object();
    public List<Test> ExecutedTests;

    public Tests()
    {
        ExecutedTests = new List<Test>();
    }
}

class UIManager
{
    public static void DisplayAllResultInUIGrid(List<Test> list)
    {
        //Code to update UI.
    }
}

Considering that the scope is to not update the UI while the other thread is adding tests to the list, it is safe to use:

lock (Tests.testLock)

or should I use:

lock (TestHolder.testLock)

(changing the static property of testLock)?

Do you think this is a good way to write this kind of program or can you suggest a better pattern?

Thank you for your help!

Daniele Arrighi
  • 139
  • 1
  • 8
  • 1
    See msdn : https://msdn.microsoft.com/en-us/library/kzy257t0(v=vs.110).aspx – jdweng Jan 11 '17 at 15:24
  • 1
    Which framework are you targeting? I ask this because if you are using 4.0 or above you should use Tasks https://msdn.microsoft.com/en-us/library/dd537609(v=vs.100).aspx – taquion Jan 11 '17 at 15:29

2 Answers2

1

Public (not talking about public static) lock objects tend to be dangerous. Please see here

The reason it's bad practice to lock on a public object is that you can never be sure who ELSE is locking on that object.

Furthermore just having a List<T> and adding objects from an outer scope could be a smell, too.

In my opinion it'd be a better idea to have a method AddTest in Tests

class Tests
{
    private static readonly object testLock = new object();
    private List<Test> executedTests;

    public Tests()
    {
        ExecutedTests = new List<Test>();
    }

    public void AddTest(Test t)
    {
        lock(testLock)
        {
            executedTests.Add(t);
        }
    }

    public IEnumerable<Test> GetTests()
    {
        lock(testLock)
        {
            return executedTests.ToArray();
        }
    }
    [...]
}

Clients of your tests class do not have to worry about using the lock object correctly. Precisely, they don't have to worry about any of the internals of your class.

You could, anyway, rename your class to ConcurrentTestsCollectionor the like, that users of the class know, that it's thread safe to some extent.

Community
  • 1
  • 1
Paul Kertscher
  • 9,416
  • 5
  • 32
  • 57
0

While you can use Tasks and the async/await keywords to do this less verbosely, I don't think it will fully solve your question.

I will assume that ExecutedTests is a List(or like) that you want to be thread safe, which is why you are creating a lock while accessing it.

I would make the list, itself, thread safe, rather than the operations against it. This will remove the need for a lock or a lock object.

You could implement this yourself or use something in the System.Collections.Concurrent namespace.

P.S.

If the threads are meant to be closed(aborted) when the process is exited you should set the Thread's IsBackground property to true.

R. McIntosh
  • 166
  • 4
  • Thank you for your reply. I think I will go this way. And, yes in the real program all thread are in background. This was just to shorten the code. Thanks! – Daniele Arrighi Jan 11 '17 at 16:06