Hello i have the following problem:
I have a class Pool
that contains a list of Connection
-s.The Connection
is a wrapper over a socket
.I somehow need to create the socket
,ConnectAsync
-it ,wrap it into a Connection
and return it to the caller.The problem is that i need this this collection to be thread-safe.
Specifically i need the collection to be thread safe when i create a new Connection
or when a Connection
calls Pool
-s Free
method.
What alternative to the lock
do i have? I have seen so far SemaphoreSlim
but i do not understand it.
Pool
internal partial class Pool {
public static Pool MakePool(UserSettings settings)
{
return new Pool(settings);
}
private List<Connection> liveConnections;
private readonly object @lock = new object();
public readonly UserSettings settings;
public async Task<Connection> ConnectAsync()
{
Connection readyConnection;
lock(@lock)
{
if (this.liveConnections == null)
{
this.liveConnections = new List<Connection>(this.settings.MIN);
}
readyConnection = this.liveConnections.FirstOrDefault(x => !x.IsUsed);
if (readyConnection == null)
{
readyConnection = await CreateConnectionAsync(settings);
this.liveConnections.Add(readyConnection);
}
return readyConnection;
}
}
private async Task<Connection> CreateConnectionAsync(UserSettings settings)
{
//Socket initialization
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPAddress address=IPAddress.Parse(settings.HOST_NAME);
int port = settings.PORT;
IPEndPoint point = new IPEndPoint(address, port);
await socket.ConnectAsync(point);
ConnectionSettings conSettings = new ConnectionSettings
{
pool = this,
ConnectionID = GenerateID(),
socket = socket,
};
Connection con= Connection.CreateConnection(conSettings);
return con;
}
//this gets called by the connection !!
internal void Free(string ID)
{
lock (@lock)
{
Connection con=this.liveConnections.Find(x => x.ID == ID);
con.IsUsed = false;
}
}
private static string GenerateID()=>Guid.NewGuid().ToString();
private Pool(UserSettings settings)
{
this.settings = settings;
}
}
Connection
public class Connection :IDisposable
{
private PhysicalConnection rawConnection;
internal static Connection CreateConnection(ConnectionSettings settings)
{
Connection con = new Connection(settings);
return new Connection(settings);
}
public readonly string ID;
private readonly Pool parentPool;
public bool IsUsed { get; internal set; }
public void Dispose()
{
this.parentPool.Free(this.ID);
}
private Connection(ConnectionSettings settings)
{
this.ID = settings.ConnectionID;
this.parentPool = settings.pool;
this.rawConnection = new PhysicalConnection(settings.socket);
}
}
ConnectionSettings
class ConnectionSettings
{
public Pool pool;
public string ConnectionID;
public Socket socket;
}
As you can see the Pool
is sent in the Connection
constructor so that the Connection
can notify the Pool
when it is disposed !