Pergunta

I'm creating a client-server structure where the server has a thread for every client. That specific thread only sends and receives data. In the main thread of the server I'd want to read the input that the clientthread received. But it's possible that that input is being modified by the clientthread at the same time the mainthread is reading. How would i prevent this? I have read on locks but have no idea how to implement them that way.

A second part of my question is: the clientthread is a loop that constantly reads from a networkstream, and thus blocks that thread until it can read something. But can i call a function from my main thread (that function would send something through that networkstream) that the existing clientthread (that is looping) must execute?

Sorry i can't give any code right now, but i think it's clear enough?

Foi útil?

Solução

It sounds like a producer-consumer design might be a good fit for your problem. In general terms, the client threads will put any received data into a (thread safe) queue and not modify it after that - any new data that arrives will go to a new slot in the queue. The main thread can then wait for new data in any of the queues and process it once it arrives. The main thread could either check on all the queues periodically, or (better) receive some sort of notification when data is placed in a queue, so that it can sleep while nothing is happening and won't eat CPU time.

Since you ask about locks: Here is a basic lock-based implementation as an alternative to queues, perhaps that will help you understand the principle

class IncomingClientData
{
    private List<byte> m_incomingData = new List<byte>();
    private readonly object m_lock = new object();

    public void Append(IEnumerable<byte> data)
    {
        lock(m_lock)
        {
            m_incomingData.AddRange(data);
        }
    }

    public List<byte> ReadAndClear()
    {
        lock(m_lock)
        {
            List<byte> result = m_incomingData;
            m_incomingData = new List<byte>();
            return result;
        }
    }
}

In this example, your client threads would call Append with the data that they have received, and the main thread could collect all the rececived data that arrived since the last check by calling ReadAndClear.

This is made thread-safe by locking all the code in both functions on m_lock, which is just a regular plain object - you can lock on any object in C#, but I believe this can be confusing and actually lead to subtle bugs if used carelessly, so I almost always use a dedicated object to lock on. Only one thread at a time can hold the lock on an object, so the code of those functions will only run in one thread at a time. For example, if your main thread calls ReadAndClear while the client thread is still busy appending data to the list, the main thread will wait until the client thread leaves the Append function.

It's not required to make a new class for this, but it can prevent accidents, because we can carefully control how the shared state is being accessed. For example, we know that it is safe to return the internal list in ReadAndClear() because there can be no other reference to the same list at that time.

Now for your second question: Just plain calling a method won't ever cause the method to run on a different thread, no matter which class the method is in. Invoke is a special feature of the WinForms UI thread, you'd have to implement that functionality yourself if you want to Invoke something in your worker threads. Internally, Invoke works by placing the code you want to run into a queue of all things that are supposed to run on the UI thread, including e.g. UI events. The UI thread itself is basically a loop that always pulls the next piece of work from that queue, and then performs that work, then takes the next item from the queue and so on. That is also why you shouldn't do long work in an event handler - as long as the UI thread is busy running your code, it won't be able to process the next items in its queue, so you'll hold up all the other work items / events that occur.

If you want your client threads to run a certain function, you have to actually provide the code for that - e.g. have the client threads check some queue for commands from the main thread.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top