使线程在c#中并行工作

时间:2009-12-21 12:06:22

标签: c# multithreading locking

我正在使用TcpClient对象来传输文件。只有一个对象。因此,在传输文件时,我曾经锁定此对象并传输文件,以便其他线程等待,直到TCPClient被释放。

有没有办法让Threads与单个对象并行工作?

3 个答案:

答案 0 :(得分:3)

不,让每个线程都使用自己的连接。

答案 1 :(得分:0)

...有点儿

你可以使用NetworkStream.BeginWrite,感觉就好像你在paeallel中使用它一样....

答案 2 :(得分:0)

BeginWrite / BeginRead将释放您当前的线程并在后台执行操作。您可以使用BeginWrite排队多次写入(并获得并行工作感)。我不建议这样做,因为内部套接字缓冲区在发送文件时可能会变满。

您始终可以切换到套接字并使用SendFile方法。

如果你有多个TcpClients或套接字,你可以使用BeginWrite / BeginRead方法来处理它们,而不必为每个方法创建一个新线程。

下面是一个小例子,它只是连接到服务器,发送文件并断开连接。它可以处理任何数量的开放tcpclients。如你所见,它不使用任何线程(它是在.Net框架的后台完成的)

/// <summary>
/// Thread safe queue of client ids
/// </summary>
internal class FileSender
{
    /// <summary>
    /// A write operation have completed 
    /// </summary>
    /// <param name="ar"></param>
    private void OnWriteCompleted(IAsyncResult ar)
    {
        // We passed the context to this method, cast it back
        var context = (ClientContext) ar.AsyncState;

        // end the write
        context.TcpClient.GetStream().EndWrite(ar);

        // we've completed.
        if (context.BytesSent >= context.FileStream.Length)
        {
            // notify any listener
            Completed(this, new CompletedEventArgs(context.RemoteEndPoint, context.FileStream.Name));
            context.TcpClient.Close();
            return;
        }

        // Send more data from the file to the server.
        int bytesRead = context.FileStream.Read(context.Buffer, 0, context.Buffer.Length);
        context.BytesSent += bytesRead;
        context.TcpClient.GetStream().BeginWrite(context.Buffer, 0, bytesRead, OnWriteCompleted, context);
    }

    /// <summary>
    /// Send a file
    /// </summary>
    /// <param name="endPoint"></param>
    /// <param name="fullPath"></param>
    public void SendFile(IPEndPoint endPoint, string fullPath)
    {
        // Open a stream to the file
        var stream = new FileStream(fullPath, FileMode.Open, FileAccess.Read);

        // Create a client and connect to remote end
        var client = new TcpClient();
        client.Connect(endPoint);

        // Context is used to keep track of this client
        var context = new ClientContext
                          {
                              Buffer = new byte[65535],
                              FileStream = stream,
                              TcpClient = client,
                              RemoteEndPoint = endPoint
                          };

        // read from file stream
        int bytesRead = stream.Read(context.Buffer, 0, context.Buffer.Length);

        // and send the data to the server
        context.BytesSent += bytesRead;
        client.GetStream().BeginWrite(context.Buffer, 0, bytesRead, OnWriteCompleted, context);
    }

    /// <summary>
    /// File transfer have been completed
    /// </summary>
    public event EventHandler<CompletedEventArgs> Completed = delegate { };

    #region Nested type: ClientContext

    /// <summary>
    /// Used to keep track of all open connections
    /// </summary>
    private class ClientContext
    {
        /// <summary>
        /// Gets or sets buffer used to send file
        /// </summary>
        public byte[] Buffer { get; set; }

        /// <summary>
        /// Gets or sets number of bytes sent.
        /// </summary>
        public int BytesSent { get; set; }

        /// <summary>
        /// Gets or sets file to send
        /// </summary>
        public FileStream FileStream { get; set; }

        public IPEndPoint RemoteEndPoint { get; set; }

        /// <summary>
        /// Gets or sets client sending the file
        /// </summary>
        public TcpClient TcpClient { get; set; }
    }

    #endregion
}

internal class CompletedEventArgs : EventArgs
{
    public CompletedEventArgs(IPEndPoint endPoint, string fullPath)
    {
        EndPoint = endPoint;
        FullPath = fullPath;
    }

    public IPEndPoint EndPoint { get; private set; }
    public string FullPath { get; private set; }
}