简体   繁体   中英

Asynchronous API for Streaming Data from a Hardware Device

I'm writing a library in C#, but I need to make it asynchronous. Normally you expose a set of DLL functions, and they take input parameters, and return a value when finished. But how can I make a library function (callable from C++/Delphi/Etc) that already starts streaming back output while still taking input?

The only solution I see now is to communicate using sockets/pipes/etc, instead of DLL calls.

Does someone have an example how to do this with normal DLL calls?

One good model for a straightforward asynchronous library call (which is located in System.dll ) is WebClient.DownloadStringAsync . This method downloads from a Uri asynchronously, and raises the DownloadStringCompleted event whenever it finishes.

Your library could likewise provide a FooAsync method, which doesn't block the current thread but raises a FooDataReceived event whenever some data comes into your library and a FooCompleted event whenever the calculation finishes.

There are a couple of ways to go with this. In most languages you can make async calls to methods using threads or dispatchers. In general, as long as you make your dll re-entrant (capable of servicing multiple threads at the same time) the calling environment can take care of the async part.

It is possible to bake the async calls into your API, however. An example of something that does this is the WCF client proxies .

Microsoft has a good article on this matter. If you just mover over the EndInvoke, it should work for you as well. http://msdn.microsoft.com/en-us/library/2e08f6yc(v=vs.71).aspx

  • Since you want both Input and Output to be async, you will need a worker thread: If neither the inputting thread, nor the one taking the output can be blocked, both can't be bothered to do the work.

  • Your already thought of communicating via pipes, but why use a pipe and not an internal strcuture?

  • So you have this lock-free queue on the input, another one on the output and a worker thread

  • The worker thread takes input from the inqueue, processes it, puts it into the outqueue

  • If the input queue becomes empty, the worker thread has nothing to crunch on, so he raises a "need more data" event and then blocks on the input queue becoming (partly) full

  • If the worker thread puts something in the output queue, he raises a "have more data" event, and if the output queue becomes (fully) full, he blocks on output space becoming available

  • Your API is nonblocking: Neither sending input nor receiving output ever block

  • Your API is async: Notifications (via Events) are given

According to the comments from the OP the calling application sends audio to the DLL, the DLL sends audio out via some USB interface, the DLL captures some audio from the mic interface and needs to send the captured audio back to the application while the application sends audio to the DLL etc.

Based on this and the fact that the calling can be written in rahter different languages I see some options for the communication channels:

  • TCP/IP (depending on "desktop firewall settings" etc. this could be problematic!)
  • Pipes
  • COM objects with events/event handlers
  • DLL with callback although this will be a bit hard to get working for all languages
  • shared memory with global mutexes (could ease that for the consuming application by offering a "setup" function from the DLL which return the pointers and mutex names)

I like the following approach because it makes it really simple for the clients.

// your library
class Foo {
   public event EventHandler ComputeCompleted = (sender, e) => { };

   public void Compute() {
      // kick off work on a background thread
      // possibly using the BackgroundWorker object
      var bw = new BackgroundWorker();      
      bw.RunWorkerCompleted += RunWorkerCompleted;
      bw.RunWorkerAsync(); 
   }

   private void RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
        ComputeCompleted(this, new object()); 
   }
}

// calling code
Foo foo = new Foo();
foo.ComputeCompleted += Completed;
foo.Compute();

private void Completed(object Sender, EventArgs e) {
   // process the result here
}

The gist is that you kick off a method in the library that returns right away, then notifies the caller via an event/delegate that the processing is complete. You are then free to Invoke the execution back onto the UI thread as needed.

Obviously, error handling not included in the sample code.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM