Question

This is my code:

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApplication4
{
    class Writer {
        public void Write(string xxx) { Console.Write(xxx); }

    }
    class Program
    {
        static Writer wrt;

        static void Main(string[] args)
        {
            wrt = new Writer();
            Thread trd = new Thread(new ThreadStart(delegate() {
                lock (wrt)
                {
                    Thread.Sleep(1000);
                    wrt.Write("1");
                }
            }));
            trd.Start();
            wrt.Write("0");
            Console.ReadLine();
        }
    }
}

The excepted output is "10", but the output is "01". Why?

Was it helpful?

Solution

You need to lock in both places:

        wrt = new Writer();
        Thread trd = new Thread(new ThreadStart(delegate() {
            lock (wrt)
            {
                Thread.Sleep(1000);
                wrt.Write("1");
            }
        }));
        trd.Start();

        lock(wrt) // Lock here, too
        {
            wrt.Write("0");
        }
        Console.ReadLine();

Using a lock doesn't prevent that instance from being used other places, it just prevents that instance from being used to acquire another lock until your lock is done.

Instead of thinking of lock as "locking the object", think of it as your object is the one and only key for that lock - no other lock(yourObject) can "unlock" that block of code until the first one is done.

Note that this will still likely show "01" as output, as it's very unlikely that the thread will startup quickly enough to get the lock first, but that's not deterministic.

OTHER TIPS

Why not use Tasks? It will ensure that the first thread is complete before continuing with the next thread.

 class Program
{
    static void Main(string[] args)
    {
        var writer = new Writer();

        var task = Task.Factory.StartNew(() =>
            {
                writer.Write("1");
            });

        task.ContinueWith((data) =>
        {
            writer.Write("0");
        });

        Console.ReadKey();
    }
}

public class Writer
{
    public void Write(string message)
    {
        Console.Write(message);
    }
}

The statement wrt.Write("0"); in the main thread is executed before the trd thread. The main thread starts the trd thread and keeps executing the statements those come under main thread thats why the Write statement under main thread is executing before trd thread.

The lock statement is inside trd thread and thus has nothing to do with wrt.Write under main thread. You can put lock on main thread as Reed Copsey suggested but you can not ensure which thread will get lock first. It could be main thread which gets the lock first.

You can ensure the trd thread finsishes execution prior to main thread by calling Thread.Join after starting thread trd, it will ensure main thread will wait until trd thread finishes the exectuion. This will ensure that you get 10 instead of 01.

static Writer wrt;  

static void Main(string[] args)
{
    wrt = new Writer();
    Thread trd = new Thread(new ThreadStart(delegate() {
        lock (wrt)
        {
            Thread.Sleep(1000);
            wrt.Write("1");
        }
    }));
    trd.Start();
    trd.Join();
    wrt.Write("0");
    Console.ReadLine();
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top