多线程访问返回方法带锁线程安全吗?

时间:2019-06-26 19:30:15

标签: c# multithreading thread-safety

我正在尝试从多个线程访问一个方法,我不确定这是否是线程安全的。如果不是,那么最安全的方法是什么?

我考虑了每个人的见解,最终产品如下所示,我还选择了对包装盒本身进行回收。

我的数据包池类

using System.Collections.Generic;

namespace Networking
{
    public class PacketPool
    {
        private Queue<Packet> pool = new Queue<Packet>();
        private readonly object instance = new object();

        public Packet CreatePacket(string method)
        {
            lock (instance)
                return pool.Count == 0 ? new Packet() { Pool = this } : 
                       pool.Dequeue();
        }

        public void Recycle(Packet packet)
        {
            lock(instance)
               pool.Enqueue(packet);
        }
    }
}

数据包类别

using System;
using System.Net;

namespace Networking
{
    public class Packet 
    {
        public Protocol Proto = Protocol.Sequenced;
        public PacketFlags Flag = PacketFlags.None;
        public Fragment Fragmented = Fragment.NotFragged;
        public SendType SendType = SendType.Raw;
        public EndPoint RemoteEp = new IPEndPoint(IPAddress.Any, 0);
        public byte[] Buffer = new byte[512];
        public int Count = 0;
        public int Ptr = 8;
        public int Bits { get; set; } = 0;
        public ushort Id = 0;
        public ushort Lead = 0;
        public PacketPool Pool;

        public void Recycle()
        {
            Bits = 0;

            if (Buffer.Length > 512)
                Array.Resize(ref Buffer, 512);

            Count = 0;
            Flag = PacketFlags.None;
            Fragmented = Fragment.NotFragged;
            Proto = Protocol.Sequenced;
            Ptr = 8;
            Lead = 0;
            SendType = SendType.Raw;
            Pool.Recycle(this);
        }
    }
}

希望上述解决方案可以使生活更轻松。

2 个答案:

答案 0 :(得分:4)

您显示的代码只要没有在单独的线程上修改数据包本身就看起来是线程安全的。您还可以考虑使PacketPool类具有线程安全性,以节省一些宝贵的锁定时间。

您可以通过将ConcurrentQueue替换为Queue来简化它。

请注意,您正在修改发送到Recycle方法中的数据包。我建议使数据对象不可变,以免发生意外行为。

public class PacketPool
{
    public ConcurrentQueue<Packet> pool = new ConcurrentQueue<Packet>(2000);

    public Packet CreatePacket(string method)
    {
        if (pool.TryDequeue(out Packet packet))
        {
            return packet;
        }

        return new Packet();
    }

    public void Recycle(Packet packet)
    {
        packet.Bits = 0;

        if (packet.Buffer.Length > 512)
            Array.Resize(ref packet.Buffer, 512);

        packet.Count = 0;
        packet.Flag = PacketFlags.None;
        packet.Fragmented = Fragment.NotFragged;
        packet.Proto = Protocol.Sequenced;
        packet.Ptr = 8;
        packet.Lead = 0;
        packet.SendType = SendType.Raw;
        pool.Enqueue(packet);
    }
}

答案 1 :(得分:1)

如果不跨线程共享数据包,则由于有锁,这是线程安全的。

请注意公共场所池。您也可以在不安全的线程上进行编辑。

请注意,建议使用专用的锁定对象,否则可能会出现死锁-例如,另一个进程锁定了公共属性Pool。建议将锁对象设为私有,并仅在该类中使用它(这样可以向上升级)

  

当您同步线程对共享资源的访问时,请锁定专用对象实例(例如,私有只读对象balanceLock = new object();)或另一个不太可能被无关部分用作锁定对象的实例代码。避免对不同的共享资源使用相同的锁对象实例,因为这可能导致死锁或锁争用。

https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/lock-statement