Thursday 27 April 2017

TPL 07 Locking

Just like signalling, locking works the same way whether using threads or tasks. Let's reuse our Straws example but this time let's leverage locking.

To get started let's set up our enum, Straws and Person classes

enum Straw { Long, Short }

class Straws
{
    static Random rnd = new Random(DateTime.Now.Millisecond);
    Straw[] _straws;
    public int length { get { return _straws.Length; } }
    public Straws(int NumOfStraws, int NumOfShortStraws = 1)
    {
        _straws = new Straw[NumOfStraws];

        while (NumOfShortStraws > 0) {
            var randomIndex = rnd.Next(NumOfStraws);
            if (_straws[randomIndex] != Straw.Short) {
                _straws[randomIndex] = Straw.Short;
                NumOfShortStraws--;
            }
        }
    }

    public Straw this[int i] {
        get {
            var selectedStraw = _straws[i];
            for (; i < _straws.Length-1; i++)
                _straws[i] = _straws[i + 1];
            Array.Resize(ref _straws, _straws.Length - 1);

            return selectedStraw;
        }
    }
}

class Person
{
    static Random rnd = new Random(DateTime.Now.Millisecond);
    public string Name { get; set; }
    public Straw? Straw { get; private set; }
    public void PickStraw(Straws Straws) Straw = Straws[rnd.Next(Straws.length)]; }
    public override string ToString() return $"{Name} picked a {Straw} straw"}
}


now let's use locking in our main thread to isolate access to our straws class.

class Program
{
    static void Main(string[] args)
    {
        var ppl = new Person[] { new Person { Name = "Pawel" },
            new Person {Name="Magda" }, new Person { Name="Tomek" },
            new Person { Name="Jakub" }, new Person { Name="Marin" } };
        var straws = new Straws(ppl.Length);
        var _lock = new object();

        using (var cdEvent = new CountdownEvent(ppl.Length))
        {
            foreach (var person in ppl)
                Task.Factory.StartNew(p => {
                    lock(_lock)
                        ((Person)p).PickStraw(straws);
                    cdEvent.Signal();
                }, person);

            cdEvent.Wait();
        }

        foreach(var p in ppl)
            Console.WriteLine(p.ToString());
    }

}

by locking down access to the straws class we can correctly distribute the straws among all of the people.