跨线程的互斥锁?

4
所以,以下代码引发了异常。我已经将异常详细信息放在了下面,但我认为这是由于释放锁的线程与启动锁的线程不同导致的。我想这样做可能会让我的主线程继续运行,而写入则排队并发生。
首先,我想知道是否有办法可以做到这一点?其次,这应该这样做还是一个坏计划?我已经看过ManualResetEvent、Monitor和Mutex,它们似乎都可以做到这一点。请注意,这是用于网络服务器的,这是我第一次编写多线程、潜在高流量的服务器。
异常:第一次调用发送(及其回调)时抛出。
System.ApplicationException在mutex.ReleaseMutex()上。"从未同步的代码块中调用了对象同步方法。"
public void SendAsync(byte[] packet)
{
    mutex.WaitOne();
    client.GetStream().BeginWrite(packet, 0, packet.Length, WriteCallback, null);
}

private void WriteCallback(IAsyncResult ar)
{
    client.GetStream().EndWrite(ar);
    mutex.ReleaseMutex();

    Console.WriteLine("Sent Data to " + RemoteEndPoint);
}
2个回答

3

假设您正在使用Mutex类,根据Microsoft的说法:

Mutex类强制执行线程标识,因此只有获取它的线程可以释放互斥体。相比之下,Semaphore类不强制执行线程标识。互斥体还可以跨应用程序域边界传递。

我认为,您的回调方法是从另一个线程调用的,这导致了异常。
根据Microsoft的建议,您可以在这种情况下使用Semaphore。

例如:

static volatile Semaphore sem = new Semaphore(1,1);
    static void Main(string[] args)
    {
        Thread oThread = new Thread(new ThreadStart(fn2));
        oThread.Start();
        Thread.Sleep(200);
        sem.WaitOne();
        Console.WriteLine("Main is Doing");
        Thread.Sleep(2000);

        sem.Release();
    }
    static void fn2()
    {
        sem.WaitOne();
        Console.WriteLine("Thread is Doing");
        Thread.Sleep(2000);

        sem.Release();
    }

谢谢你的回答!我想我试过了,但为了实验目的,我会再试一次。 - guitar80

2

考虑到这一点 -

我希望通过这种方式,让我的主线程在写入排队和发生时继续运行。

首先,我想知道是否有方法可以实现这一点?

我认为你不需要使用mutex。你是想要类似于这样的东西吗 -

static object _lock = new object(); //somewhere based on your context
public void SendAsync(byte[] packet)
{
    Task.Run(() =>
        {
            ..... other codes
            lock (_lock)
            {
                client.GetStream().BeginWrite(packet, 0, packet.Length, 
                ar => // the write callback (lambda)
                {
                    client.GetStream().EndWrite(ar);
                    Console.WriteLine("Sent Data to " + RemoteEndPoint);
                }, null);
            }
        });
}

解释:

  1. Task.Run 在线程池中异步运行,使您的主线程始终保持空闲。
  2. lock(_lock) 确保只有一个线程在任何单一时刻写入流(线程由来自Task.Runthreadpool生成)。
  3. 您不需要单独的writecallback,可以使用内联的lambda callback方法。这样会让生活变得更加轻松。

如果这解决了您的问题,请告诉我。


这似乎不是一个很好的设计。在这里,一个将执行异步化的任务正在使用锁来同步共享资源,从而限制了所有处理器上的所有任务的工作,并在内部再次调用另一个异步方法,它依赖于IAsyncResult来显示结果。 - Mrinal Kamboj
1
这是因为OP想要“我想这样做可能会让我的主线程继续运行,同时写入排队并发生。” 没有锁定,流将产生意外输出。 - brainless coder
4
它能够实现目标,但由于需要为高流量系统设计,任务内部的锁定将阻塞所有其他任务,这将导致非常高的争用并严重降低性能。任务足以使事物异步化,最基本的做法是仅在写入流的部分上加锁,而且仅当绝对必要时才这样做,否则它会失去意义。检查是否可以进行一些数据并行化,并且可以在不使用同步构造(如锁定、互斥体)的情况下实现目标。 - Mrinal Kamboj
1
@user3812869 Mrinal说得有道理 - 如果你期望有很多并行调用SendAsync,那么使用上述解决方案会冒着高锁竞争和性能下降的风险。你可能需要重新设计代码,使每个线程都使用自己的client实例 - 从而消除锁定的需要。 - anikiforov
谢谢大家的建议!我会仔细研究,但是我不确定如何完全避免使用锁,因为这些线程将共享资源,但也许我可以尽量减少使用。 - guitar80
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接