在多线程中传递数据,免不了要用生产者,消费者模式,几个例子:
Java:
public class BlockingQueueInputStream extends InputStream {
private static final String TAG = "BlockingQueueIS";
private BlockingQueue<byte[]> mData = null;
private byte[] mDataCache = null;
private boolean mFinished = false;
public BlockingQueueInputStream(int capacity) {
mData = new LinkedBlockingQueue<>(capacity);
mFinished = false;
}
public BlockingQueueInputStream(BlockingQueue<byte[]> blockingQueue) {
mData = blockingQueue;
}
public void reset() {
mFinished = false;
}
public BlockingQueue<byte[]> getBlockingQueue() {
return mData;
}
public int write(byte[] buffer) {
if(mFinished) {
return -1;
}
try {
// a deep copy since the blocking queue only store reference
byte[] temp = Arrays.copyOf(buffer, buffer.length);
mData.put(temp);
return temp.length;
} catch (InterruptedException e) {
Dbg.e(TAG, "Write Exception", e);
return -1;
}
}
public int read() {
throw new UnsupportedOperationException("Single-byte read not supported");
}
public int read(byte[] b) throws IOException {
return read(b, 0, b.length);
}
public int read(byte[] s, int offset, int length) throws IOException {
if (mDataCache != null && mDataCache.length >= length) {
System.arraycopy(mDataCache, 0, s, offset, length);
byte[] newDataCache = new byte[mDataCache.length - length];
System.arraycopy(mDataCache, length, newDataCache, 0, newDataCache.length);
mDataCache = newDataCache;
return length;
}
int total_read = 0;
if (mDataCache != null && mDataCache.length < length) {
System.arraycopy(mDataCache, 0, s, offset, mDataCache.length);
total_read += mDataCache.length;
mDataCache = null;
}
while (total_read < length) {
try {
byte[] bytes = null;
while (!mFinished || !mData.isEmpty()) {
bytes = mData.poll(100, TimeUnit.MILLISECONDS);
if (bytes != null) {
break;
}
}
if (bytes == null) {
if (total_read > 0) {
return total_read;
} else {
return -1;
}
}
// We have extra data, save it to the cache and waiting for next read.
if (total_read + bytes.length > length) {
mDataCache = new byte[total_read + bytes.length - length];
System.arraycopy(bytes, bytes.length - mDataCache.length, mDataCache, 0, mDataCache.length);
System.arraycopy(bytes, 0, s, total_read + offset, bytes.length - mDataCache.length);
return length;
} else {
System.arraycopy(bytes, 0, s, offset + total_read, bytes.length);
total_read += bytes.length;
}
} catch (InterruptedException e) {
Dbg.e(TAG, "Read Exception", e);
return -1;
}
}
return total_read;
}
public void close() {
mFinished = true;
mData.clear();
}
}
Java
public class MyBlockingQueue {
private Queue queue = new LinkedList();
/**
* Make a blocking Dequeue call so that we'll only return when the queue has
* something on it, otherwise we'll wait until something is put on it.
*
* @returns This will return null if the thread wait() call is interrupted.
*/
public synchronized Object dequeue() {
Object msg = null;
while (queue.isEmpty()) {
try {
wait(100);
} catch (InterruptedException e) {
// Error return the client a null item
return msg;
}
}
msg = queue.remove();
Log.i("myblocking", "queue poll " + queue.size());
return msg;
}
/**
* Enqueue will add an object to this queue, and will notify any waiting
* threads that there is an object available.
*/
public synchronized void enqueue(Object o) {
queue.add(o);
Log.i("myblocking", "queue put " + queue.size());
// Wake up anyone waiting for something to be put on the queue.
notifyAll();
}
public boolean isEmpty() {
return queue.isEmpty();
}
}
使用OKIO的
public class FifoInputStream extends InputStream {
private static final String TAG = FifoInputStream.class.getSimpleName();
int mSampleRate;
int mDelay;
private Pipe mPipe;
private volatile boolean mStopped = false;
public FifoInputStream(int sampleRate, int delay) {
mSampleRate = sampleRate;
mDelay = delay;
mStopped = false;
mPipe = new Pipe(mSampleRate * 2 * 10); //10s Buffer
mPipe.sink().timeout().timeout(10000, TimeUnit.MILLISECONDS);
mPipe.source().timeout().timeout(10000, TimeUnit.MILLISECONDS);
}
@Override
public int read() throws IOException {
return 0;
}
public void write(byte[] buffer) throws IOException {
if (mStopped) {
Log.i(TAG, "write stopped");
return;
}
int length = buffer.length;
int available = length;
if (mDelay > 0) {
mDelay = mDelay - buffer.length * 1000 / (2 * mSampleRate);
} else {
while (available > 0 && !mStopped) {
Buffer temp = new Buffer();
temp.write(buffer, length - available, available);
Log.i(TAG, "write " + available);
mPipe.sink().write(temp, available);
}
}
}
@Override
public int read(byte[] buffer, int offset, int length) throws IOException {
if(mStopped) {
Log.i(TAG, "read stopped -1");
return -1;
}
Buffer temp = new Buffer();
// return -1 when exhausted after sink closed; if length < available, return available immediately
long i = mPipe.source().read(temp, length);
if (i != -1) {
temp.read(buffer, offset, (int) i);
} else {
mPipe.source().close();
}
Log.i(TAG, "Fifo read " + i);
return (int) i;
}
public void close() throws IOException {
Log.i(TAG, "Fifo close");
mStopped = true;
mPipe.sink().close();
mPipe.source().close();
}
}
C++
class RingBuffer {
private:
int8_t * pBase;
int32_t mLength;
//pRead point to the first address of real data
int8_t * pRead = NULL;
//pWrite point to the address after the last real data
int8_t * pWrite = NULL;
pthread_mutex_t ringbuffer_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t ringbuffer_written_cond = PTHREAD_COND_INITIALIZER;
int32_t getFreeCount();
int32_t getDataCount();
public:
RingBuffer(int length);
~RingBuffer();
int32_t read(void * buffer, int32_t size);
int32_t write(void * buffer, int32_t size);
};
int32_t mobvoi::RingBuffer::getDataCount() {
int32_t count = pWrite - pRead;
if (count < 0) {
count += mLength;
}
return count;
}
int32_t mobvoi::RingBuffer::getFreeCount() {
int count = 0;
if (pRead > pWrite) {
count = pRead - pWrite - 1;
} else {
count = mLength - (pWrite - pRead + 1);
}
return (count > 0) ? count : 0;
}
// read from ring buffer to liner buffer, blocking
int32_t mobvoi::RingBuffer::read(void * buffer, int32_t count) {
pthread_mutex_lock(&ringbuffer_mutex);
if (count >= mLength) {
pthread_mutex_unlock(&ringbuffer_mutex);
return -1;
}
while (count > getDataCount()) {
pthread_cond_wait(&ringbuffer_written_cond, &ringbuffer_mutex);
}
if (pRead <= pWrite) {
memcpy(buffer, pRead, count);
pRead += count;
} else {
int8_t * end = pBase + mLength;
int32_t r2e = end - pRead;
if (count <= r2e) {
memcpy(buffer, pRead, count);
pRead += count;
if (pRead == end) {
pRead = pBase;
}
} else {
memcpy(buffer, pRead, r2e);
memcpy(buffer + r2e, pBase, count - r2e);
pRead = pBase + count - r2e;
}
}
pthread_mutex_unlock(&ringbuffer_mutex);
return count;
}
// write data from liner buffer to ring buffer, non blocking
int32_t mobvoi::RingBuffer::write(void * buffer, int32_t count) {
pthread_mutex_lock(&ringbuffer_mutex);
int32_t space = getFreeCount();
int32_t writeCount;
if (space == 0) {
pthread_mutex_unlock(&ringbuffer_mutex);
return 0;
} else {
writeCount = count <= space ? count : space;
}
int8_t * end = pBase + mLength;
if (pRead <= pWrite) {
int32_t w2e = end - pWrite;
if (writeCount <= w2e) {
memcpy(pWrite, buffer, writeCount);
pWrite += writeCount;
if (pWrite == end) {
pWrite = pBase;
}
} else {
memcpy(pWrite, buffer, w2e);
memcpy(pBase, buffer + w2e, writeCount - w2e);
pWrite = pBase + writeCount - w2e;
}
} else {
memcpy(pWrite, buffer, writeCount);
pWrite += writeCount;
}
pthread_cond_signal(&ringbuffer_written_cond);
pthread_mutex_unlock(&ringbuffer_mutex);
return writeCount;
}
mobvoi::RingBuffer::RingBuffer(int length) {
pBase = new int8_t[length + 1];
mLength = length + 1;
pRead = pBase;
pWrite = pBase;
}
mobvoi::RingBuffer::~RingBuffer() {
delete pBase;
pBase = NULL;
mLength = 0;
pRead = NULL;
pWrite = NULL;
}
Queue
class Queue {
private:
int MAX_SIZE;
float * A;
int front, rear;
public:
// Constructor - set front and rear as -1.
// We are assuming that for an empty Queue, both front and rear will be -1.
Queue(int size) {
MAX_SIZE = size;
front = -1;
rear = -1;
A = new float(MAX_SIZE);
}
~Queue() {
free(A);
}
// To check wheter Queue is empty or not
bool IsEmpty() {
return (front == -1 && rear == -1);
}
// To check whether Queue is full or not
bool IsFull() {
return (rear + 1) % MAX_SIZE == front ? true : false;
}
// Inserts an element in queue at rear end
void Enqueue(float x) {
if (IsFull()) {
//return;
Dequeue();
}
if (IsEmpty()) {
front = rear = 0;
} else {
rear = (rear + 1) % MAX_SIZE;
}
A[rear] = x;
}
// Removes an element in Queue from front end.
void Dequeue() {
if (IsEmpty()) {
return;
} else if (front == rear) {
rear = front = -1;
} else {
front = (front + 1) % MAX_SIZE;
}
}
// Returns element at front of queue.
int Front() {
if (front == -1) {
//cout<<"Error: cannot return front from empty queue\n";
return -1;
}
return A[front];
}
float getMax() {
if (IsEmpty()) {
return -1;
}
float temp = A[front];
int count = (rear + MAX_SIZE - front) % MAX_SIZE + 1;
//std::cout<<"Queue : ";
for (int i = 0; i < count; i++) {
int index = (front + i) % MAX_SIZE; // Index of element while travesing circularly from front
//std::cout<<A[index]<<" ";
if (temp < A[index]) {
temp = A[index];
}
}
return temp;
}
/*
Printing the elements in queue from front to rear.
This function is only to test the code.
This is not a standard function for Queue implementation.
*/
void Print() {
// Finding number of elements in queue
int count = (rear + MAX_SIZE - front) % MAX_SIZE + 1;
//std::cout<<"Queue : ";
for (int i = 0; i < count; i++) {
int index = (front + i) % MAX_SIZE; // Index of element while travesing circularly from front
//std::cout<<A[index]<<" ";
}
//std::cout<<"MAX " << getMax()<<"\n\n";
}
};