C# 多线程之间的同步

C# 多线程之间的同步,c#,multithreading,synchronization,C#,Multithreading,Synchronization,我有几个线程被称为两个或多个方法。我需要同步它们,所以我尝试使用barrierclass: Barrier barrier = new Barrier(2); // 2 = #threads participating. bool complete = false; TaskFactory factory = Task.Factory; // Start tasks Task task_1 = factory.StartNew(() => { process_1.Server("

我有几个线程被称为两个或多个方法。我需要同步它们,所以我尝试使用
barrier
class:

Barrier barrier = new Barrier(2); // 2 = #threads participating.
bool complete = false;
TaskFactory factory = Task.Factory;

// Start tasks
Task task_1 = factory.StartNew(() =>
{
    process_1.Server("1 and 2");
    barrier.SignalAndWait(); // Wait for task 2 to catch up.
    barrier.SignalAndWait(); // Wait for task 2 to print "2" and set complete = true.

    if (complete)
    {
        process_1.Server("1 and 3");
    }
});
Task task_6 = factory.StartNew(() =>
{
    process_6.Server("6 and 4");
    process_6.Server("6 and 3");
});
Task task_2 = factory.StartNew(() =>
{
    barrier.SignalAndWait(); // Wait for task 1 to print "1".
    process_2.Client("1 and 2");
    complete = true;
    barrier.SignalAndWait(); // Wait for task 1 to read complete as true.

    process_2.Server("2 and 5");
    process_2.Server("2 and 3");
});
Task task_4 = factory.StartNew(() =>
{
    process_4.Client("6 and 4");
    process_4.Server("4 and 7");
    process_4.Server("4 and 3");
});
Task task_5 = factory.StartNew(() =>
{
    process_5.Client("2 and 5");
    process_5.Server("5 and 3");
});
Task task_7 = factory.StartNew(() =>
{
    process_7.Client("4 and 7");
    process_7.Server("7 and 3");
});
Task task_3 = factory.StartNew(() =>
{
    process_3.Client("1 and 3");
    process_3.Client("2 and 3");
    process_3.Client("4 and 3");
    process_3.Client("5 and 3");
    process_3.Client("6 and 3");
    process_3.Client("7 and 3");
});

task_3.Wait();
class Pipe
{
    internal static int counter = 0;
    private readonly int id = counter++;
    private readonly IList<string> calls;
    public Pipe(IList<string> calls) { this.calls = calls; }
    public virtual void Server(string s) { EnqueeCall(s, "server"); }
    public virtual void Client(string s) { EnqueeCall(s, "client"); }
    private void EnqueeCall(string s, string actor)
    {
        calls.Add(actor + id + " processes " + s);
    }
}
我需要确保从不同线程调用方法之间的结果,例如:
process_1.Server(“1和2”)
过程2.客户(“1和2”)。在
Server
之前调用
Client
方法是不可接受的。所有依赖项:
{process_1.Server(“1和2”);process_2.Client(“1和2”)}、{process_2.Server(“2和5”);process_5.Client(“2和5”)}、{process_6.Server(“6和4”);process_4.Client(“6和4”)}、{process process_4.Server(“4和7”);process_7.Client(“4和7”)}、{process process 1.Server(“1和3”);process,{进程2.服务器(“2和3”);进程3.客户端(“2和3”)},{进程4.服务器(“4和3”);进程3.客户端(“4和3”)},{进程5.服务器(“5和3”);进程3.客户端(“5和3”)},{进程6.服务器(“6和3”);进程3.客户端(“6和3”)},{进程7.服务器(“7和3”);进程3.客户端(“7和3”)}。

在元素
{…}
{…}
之间没有依赖关系。因此,可以执行
{process_6.Server(“6和3”);process_3.Client(“6和3”)}、{process_7.Server(“7和3”);process_3.Client(“7和3”)}
或反之亦然
{process process_7.Server(“7和3”);process 3.Client(“7和3”)}、{process u 6.Server(“6和3”);进程3.Client(“6和3”)}
。我编写的
{…}
中的元素之间存在依赖关系。你能帮我解决这个问题吗?我不知道如何实现它

多谢各位

完整程序代码:

class Pipe
{
    public string message;

    public Pipe()
    {
        message = "";
    }

    public Pipe(string message)
    {
        this.message = message;
    }

    public void Server(object pipeName)
    {
        // Create a name pipe
        using (NamedPipeServerStream pipeStream = new NamedPipeServerStream(pipeName.ToString()))
        {
            // Wait for a connection
            pipeStream.WaitForConnection();

            using (StreamWriter sw = new StreamWriter(pipeStream))
            {
                sw.AutoFlush = true;
                sw.WriteLine(message);
            }
        }

        Console.Write("Communication between processes " + pipeName.ToString());
    }

    public void Client(object pipeName)
    {
        using (NamedPipeClientStream pipeStream = new NamedPipeClientStream(pipeName.ToString()))
        {
            // The connect function will indefinately wait for the pipe to become available
            // If that is not acceptable specify a maximum waiting time (in ms)
            pipeStream.Connect();

            using (StreamReader sr = new StreamReader(pipeStream))
            {
                // We read a line from the pipe and print it together with the current time
                message += sr.ReadLine();
            }
        }

        Console.WriteLine(": client received message.\n");
    }

    static void Main(string[] args)
    {

            Pipe process_1 = new Pipe("Test message from process #1.");
            Pipe process_2 = new Pipe();
            Pipe process_3 = new Pipe();
            Pipe process_4 = new Pipe();
            Pipe process_5 = new Pipe();
            Pipe process_6 = new Pipe("Test message from process #6.");
            Pipe process_7 = new Pipe();

            TaskFactory factory = Task.Factory;

            // Start tasks
            Task task_1 = factory.StartNew(() => { process_1.Server("1 and 2"); process_1.Server("1 and 3"); });
            Task task_6 = factory.StartNew(() => { process_6.Server("6 and 4"); process_6.Server("6 and 3"); });
            Task task_2 = factory.StartNew(() => { process_2.Client("1 and 2"); process_2.Server("2 and 5"); process_2.Server("2 and 3"); });
            Task task_4 = factory.StartNew(() => { process_4.Client("6 and 4"); process_4.Server("4 and 7"); process_4.Server("4 and 3"); });
            Task task_5 = factory.StartNew(() => { process_5.Client("2 and 5"); process_5.Server("5 and 3"); });
            Task task_7 = factory.StartNew(() => { process_7.Client("4 and 7"); process_7.Server("7 and 3"); });
            Task task_3 = factory.StartNew(() => { process_3.Client("1 and 3"); process_3.Client("2 and 3"); process_3.Client("4 and 3"); process_3.Client("5 and 3"); process_3.Client("6 and 3"); process_3.Client("7 and 3"); });

            task_3.Wait();
    }
}

您可能想创建一个自定义的
同步上下文及其自己的任务队列。然后,您可以根据任务的依赖关系选择要执行的任务。这可以向您展示如何设置自定义上下文以及如何使用它。

如果我理解正确,您需要确保从未调用过method
Client
是在对
Pipe
对象调用
Server
之前执行的。我已将您的示例简化为,并添加了一个测试类来记录该行为。简化后的代码包含一个更简单的
Pipe
类形式,现在它只将一些字符串放在作为参数传入的列表中e c'tor,而不是创建真正的管道

同步完全由
Pipe
的装饰子类
BlockingPipe
处理
BlockingPipe
使用一些称为条件锁或条件同步的低级机制。Jeff Magee和Jeff Kramer写了一本关于并发模式及其在java中的应用的好书c#的ok at or for c#特别是看看@john skeet的答案,他指出了另一个很好的参考。该模式包括使用通知所有等待的线程

足够的理论,回到你的代码。下面是简化的
Pipe
类:

Barrier barrier = new Barrier(2); // 2 = #threads participating.
bool complete = false;
TaskFactory factory = Task.Factory;

// Start tasks
Task task_1 = factory.StartNew(() =>
{
    process_1.Server("1 and 2");
    barrier.SignalAndWait(); // Wait for task 2 to catch up.
    barrier.SignalAndWait(); // Wait for task 2 to print "2" and set complete = true.

    if (complete)
    {
        process_1.Server("1 and 3");
    }
});
Task task_6 = factory.StartNew(() =>
{
    process_6.Server("6 and 4");
    process_6.Server("6 and 3");
});
Task task_2 = factory.StartNew(() =>
{
    barrier.SignalAndWait(); // Wait for task 1 to print "1".
    process_2.Client("1 and 2");
    complete = true;
    barrier.SignalAndWait(); // Wait for task 1 to read complete as true.

    process_2.Server("2 and 5");
    process_2.Server("2 and 3");
});
Task task_4 = factory.StartNew(() =>
{
    process_4.Client("6 and 4");
    process_4.Server("4 and 7");
    process_4.Server("4 and 3");
});
Task task_5 = factory.StartNew(() =>
{
    process_5.Client("2 and 5");
    process_5.Server("5 and 3");
});
Task task_7 = factory.StartNew(() =>
{
    process_7.Client("4 and 7");
    process_7.Server("7 and 3");
});
Task task_3 = factory.StartNew(() =>
{
    process_3.Client("1 and 3");
    process_3.Client("2 and 3");
    process_3.Client("4 and 3");
    process_3.Client("5 and 3");
    process_3.Client("6 and 3");
    process_3.Client("7 and 3");
});

task_3.Wait();
class Pipe
{
    internal static int counter = 0;
    private readonly int id = counter++;
    private readonly IList<string> calls;
    public Pipe(IList<string> calls) { this.calls = calls; }
    public virtual void Server(string s) { EnqueeCall(s, "server"); }
    public virtual void Client(string s) { EnqueeCall(s, "client"); }
    private void EnqueeCall(string s, string actor)
    {
        calls.Add(actor + id + " processes " + s);
    }
}
最后一步是测试类

[TestClass]
public class SomeTestClass
{
    [TestMethod]
    public void TestMethod()
    {
        for (var i = 0; i < 100; i++) Test();
    }

    private static void Test()
    {
        Pipe.counter = 0;
        var list = new List<string>();
        var p = new BlockingPipe(list);
        var f = Task.Factory;
        var b = new Barrier(3);
        f.StartNew(() => { p.Client("asdf"); b.SignalAndWait(); });
        f.StartNew(() => { p.Server("qwer"); b.SignalAndWait(); });
        b.SignalAndWait();
        var exp = String.Join("\n", 
          new[] { "server0 processes qwer", "client0 processes asdf" });
        var act = String.Join("\n", list);
        Assert.AreEqual(exp, act);
    }
}
[TestClass]
公共类SomeTestClass
{
[测试方法]
公共void TestMethod()
{
对于(var i=0;i<100;i++)Test();
}
专用静态空隙试验()
{
Pipe.counter=0;
var list=新列表();
var p=新堵塞管(列表);
var f=任务工厂;
var b=新屏障(3);
f、 StartNew(()=>{p.Client(“asdf”);b.SignalAndWait();});
f、 StartNew(()=>{p.Server(“qwer”);b.SignalAndWait();});
b、 SignalAndWait();
var exp=String.Join(“\n”,
新[]{“server0进程qwer”、“client0进程asdf”});
var act=String.Join(“\n”,列表);
主张平等(exp,act);
}
}
Test
方法可以被调用任意多次(希望如此),以始终产生正确的行为。我希望这可以扩展到您的用例中。测试将检查在
管道上执行的调用是否是这种形式:

server0进程qwer client0处理asdf


两个创建的线程共享同一个
Pipe
对象实例。为了测试此解决方案的健壮性,我添加了一个for循环,调用实际的
test
方法100次,始终产生相同的结果。我注意到的唯一缺陷是条件同步模式本身的实现没有实现rk而不在
监视器上添加超时。在
阻塞管道
循环中等待
调用。因为我有一个线程一直在等待接收
脉冲
,但脉冲线程已经返回,这可能需要为这种情况添加另一个条件。

您能给我一个提示吗f解释变量
process\n
的类型是什么?它是一个自己编写的类,包含
Server
Client
两个方法,其中
string
作为sincle输入参数和
void
返回值?您是否尝试过先将问题最小化,例如仅从两个线程开始,然后使用B通过添加更多线程来增加复杂性?我同意@isi,在阅读问题时我的大脑翻了个底朝天…我添加了完整程序的代码。是的,您理解正确:它是一个由我自己编写的类,包含两个方法服务器和客户端,字符串作为sincle输入参数,void返回value@HABJAN,对不起,我试过和你约会简单地说我的任务。问题是:如果我在主类中添加cycle,有时客户端方法在服务器之前启动,或者两台服务器在客户端之前启动。因此,在控制台中,我在进程1和3之间进行
通信,而不是进程1和3之间进行
通信:客户端接收ved消息。
你能帮我同步这些任务吗?我读了这篇文章。但我不知道如何同步这些任务