Проблема с блокировкой - Несколько потоков делают LOOP вне последовательности

У меня есть пример приложения, и мне интересно, может ли кто-нибудь пролить свет на это. Когда я ставлю точку останова в цикле for на шаг к коду в отладчике, почему он переключается с одного потока на другой? это происходит при запуске приложения во время выполнения... См. код ниже:

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

namespace TPLSample
{
    class Program
    {

        static void Main(string[] args)
        {
           Management m = new Management();

           Task a = new Task(() => m.Operation1());
           a.Start();
           Task b = new Task(() => m.Operation2());
           b.Start();


            Console.ReadLine();
        }
    }
    public class Management
    {
        A a = null;
        B b = null;

        public void Operation1()
        {
            a = new A();

        }
        public void Operation2()
        {
            b = new B();
        }
    }
    public class A
    {  Client a = new Client();
        public A()
        {
            while (true)
            {
                a.Test("Im AAAAA");
            }
        }
    }
    public class B
    {
        Client a = new Client();
        public B()
        {
            while (true)
            {
                a.Test("Im BBBBB");
            }
        }
    }
    public class Client
    {
        Object ibj = new Object();


        public void Test(string item)
        {
            lock (ibj)
            {
                for (int i = 0; i < 200000; i++)
                {
                    Console.WriteLine(item);
                }
            }

        }
    }
}

В результате получается смесь As и Bs. Разве блокировка не должна блокировать поток, чтобы результат был упорядочен? То же самое происходит в приложении, которое я пишу, за исключением того, что циклы WHILE выполняются вечно (каждая задача должна постоянно опрашиваться). Обратите внимание, что я запускаю две задачи, потому что я не хочу, чтобы один цикл WHILE препятствовал запуску другого, если цикл WHILE должен выполняться вечно. Как мне заставить их последовательно переходить к этой функции в классе CLIENT?


person Mage    schedule 16.05.2012    source источник
comment
Каждый объект имеет свой замок. Если бы они пытались получить одинаковую блокировку, результат был бы сериализован.   -  person jalf    schedule 16.05.2012


Ответы (2)


Это происходит потому, что если вы внимательно отследите дерево создания объектов, вы заметите, что каждый поток использует другой объект блокировки, что делает ваш оператор lock бесполезным.

Создание вашего объекта происходит следующим образом:

                 new A() -> new Client() -> new Object()
                / 
new Management()
                \
                 new B() -> new Client() -> new Object()

Вы используете последние два объекта справа в качестве замков, и вы можете ясно видеть, что это разные объекты.

Попробуйте сделать блокировку статической:

public class Client
{
    static Object ibj = new Object();
    ...

или иным образом переосмыслить свою иерархию, чтобы передать одну и ту же блокировку обеим задачам.

person Tudor    schedule 16.05.2012
comment
Это помогло и в другом приложении, которое я написал. Спасибо за вашу помощь - person Mage; 16.05.2012
comment
+1: хорошее использование символов «\» и «/». Возможно, мне тоже придется украсть эту идею для ответов :) - person Brian Gideon; 17.05.2012

Каждый объект должен иметь доступ к одному и тому же объекту, чтобы блокировка работала. Вы можете сделать это, сделав объект ibj статическим или передав его в класс Client.

public class Client
{
    static Object ibj = new Object();


    public void Test(string item)
    {
        lock (ibj)
        {
            for (int i = 0; i < 200000; i++)
            {
                Console.WriteLine(item);
            }
        }

    }
}
person csharptest.net    schedule 16.05.2012