C # asynchronous case a

Scenes

  Producer and consumer queue, there are multiple producers, consumers also have multiple, asynchronous production to consumption needs.

The following Asp.NetCore Web-API with a project to simulate

  Create two API, a Get (), a Set (), Get returns a string, Set into a string, Get Set is returned into the string.

  To achieve the following:  

[Route("api/[controller]/[action]")]
public class FooController : Control
{
    IMessageQueue _mq;
    public FooController(IMessageQueue mq)
    {
        _mq = mq;
    }

    [HttpGet]
    public string Get()
    {
        string str = _mq.ReadOne<string>();
        return str;
    }

    [HttpGet]
    public void Set(string v)
    {
        _mq.WriteOne(v);
    }
}

public interface IMessageQueue
{
    T ReadOne<T>();
    void WriteOne<T>(T value);
}

public class MessageQueue: IMessageQueue
{
    private object _value;

    public T ReadOne<T>()
    {
        return (T)_value;
    }

    public void WriteOne<T>(T value)
    {
        _value = value;

    }
}

Next, in the StartUp to the IMessageQueue injected.

services.AddSingleton<IMessageQueue, MessageQueue>();

After the run, first call / API / foo / the SET /? V = xxx , then call / api / foo / get /

We can see a successful return to the xxx

The second step, value field queue to:

So that the value is not set to go to the next cover, get queue before takes a value most

For thread-safe, used here ConcurrentQueue <T>

code show as below:

public class MessageQueue: IMessageQueue
{
    private readonly ConcurrentQueue<object> _queue = new ConcurrentQueue<object>();

    public T ReadOne<T>()
    {
        _queue.TryDequeue(out object str);
        return (T)str ;
    }

    public void WriteOne<T>(Tvalue)
    {
        _queue.Enqueue(value);
    }
}

In this case then, as long as the get is constantly polling, the data set can be taken out of production.

Call / api / foo / set /

Third, blocking asynchronous

Then the increased demand, reverse the order of get and set, after the first set simulated asynchronous get, (demo I have here is that there will be a web-api http request timeout and the like ... pretend does not exist) I want to get to call wait for data when returned.

That I want to enter in the browser address bar http: // localhost: 5000 / API / foo / GET / after constantly circling until I put a value set Interface

Scheme A: while (true), simply ruthless simply invincible, death, etc. Read () = when null break; to prevent mononuclear full turn add Thread.Sleep ();!

Scheme B: Monitor, a the Wait () a Exit / Release ();

However, these two programs are based on the Thread, after .Net4.0 accompanied ConcurrentQueue one up there is a BlockingCollection <T> is quite easy to use

Case C: modified code is as follows:

public class MessageQueue : IMessageQueue
{
    private readonly BlockingCollection<object> _queue = new BlockingCollection<object>(new ConcurrentQueue<object>());

    public T ReadOne<T>()
    {
        var obj = _queue.Take();
        return (T)obj;
    }

    public void WriteOne<T>(T value)
    {
        _queue.Add(value);
    }
}

At this point, if you first get, it will block waiting for set; If you already have a set of data will be returned through direct data queue will not get failed based on the type, can achieve more decent subscription model.

RPC extension

set here is the producer, get a consumer, that if my producer is not simply to produce this data but need to wait for a return to void the results of it? subscription model at this time is not enough, I need an asynchronous RPC.

Ask For example, there is a request to initiate the request carries parameters, and waits, knowing that there is a place to handle the additional task to produce results, ask end waiting to return results answer. 

I can go back and continue with Option A or B, but even .net4.0 are long gone, so it should be better based on asynchronous program with Task of.

Code below, the first two new interfaces:

public interface IMessageQueue
{
    void Respond<TRequest, TResponse>(Func<TRequest, TResponse> func);
    Task<TResponse> Rpc<TRequest, TResponse>(TRequest req);

    T ReadOne<T>();
    void WriteOne<T>(T data);
}

Then define a specific task categories:

public class RpcTask<TRequest, TResponse>
{
    public TaskCompletionSource<TResponse> Tcs { get; set; }
    public TRequest Request { get; set; }
}

Realize just added two new interfaces:

public Task<TResponse> Rpc<TRequest, TResponse>(TRequest req)
{
    TaskCompletionSource<TResponse> tcs = new TaskCompletionSource<TResponse>();
    _queue.Add(new RpcTask<TRequest, TResponse> { Request = req, Tcs = tcs});
    return tcs.Task;
}

public void Respond<TRequest, TResponse>(Func<TRequest, TResponse> func)
{
    var obj = _queue.Take();
    if(obj is RpcTask<TRequest, TResponse> t)
    {
        var response = func(t.Request);
        t.Tcs.SetResult(response);
    }
}

Also, write two Web API interfaces, a request awaiting the outcome of a charge of processing

[HttpGet]
public async Task<string> Ask(string v)
{
    var response = await _mq.Rpc<MyRequest, MyResponse>(new MyRequest { Id = v });
    return $"[{response.DoneTime}] {response.Id}";
}

[HttpGet]
public void Answer()
{
    _mq.Respond<MyRequest, MyResponse>((req)=> new MyResponse { Id = req.Id, DoneTime = DateTime.Now });
}

The above also casually write two class as the request and return

public class MyRequest
{
    public string Id { get; set; }
}
public class MyResponse
{
    public string Id { get; set; }
    public DateTime DoneTime { get; set; }
}

Test, three open browser tabs or postman, each initiate a request to Ask interface parameters v respectively 123, three tabs are waiting to start turning in circles

Then open a tab interface to access the answer, just put the queue processing tasks among the three options before initiating a card there is a stop waiting and display the returned data. Demand implementation.

The key type used here is TaskCompletionSource <T> .

And then expanded

If it is a distributed system, and request processing logic in a program is not it? Well, this may also be a separate queue service. At this point we must return plus a queue, and give each task queue transmission marked Id, return Id for the queue to find the TCS.SetResult () returns after removal

 

 

 

 

  

Guess you like

Origin www.cnblogs.com/pasoraku/p/11971944.html