先来了解下线程安全和线程同步,此处摘自网上说的比较容易理解的一段话:假如把整条道路看成是一个【进程】的话,那么马路中间白色虚线分隔开来的各个车道就是进程中的各个【线程】了。
①这些线程(车道)共享了进程(道路)的公共资源(土地资源)。
②这些线程(车道)必须依赖于进程(道路),也就是说,线程不能脱离于进程而存在(就像离开了道路,车道也就没有意义了)。
③这些线程(车道)之间可以并发执行(各个车道你走你的,我走我的),也可以互相同步(某些车道在交通灯亮时禁止继续前行或转弯,必须等待其它车道的车辆通行完毕)。
④这些线程(车道)之间依靠代码逻辑(交通灯)来控制运行,一旦代码逻辑控制有误(死锁,多个线程同时竞争唯一资源),那么线程将陷入混乱,无序之中。
⑤这些线程(车道)之间谁先运行是未知的,只有在线程刚好被分配到[CPU时间]片(交通灯变化)的那一刻才能知道。
C++中的线程
1)线程的基本使用
#include <jni.h>
#include <pthread.h>
#include <string>
#include <android/log.h>
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,"JNI",__VA_ARGS__);
JavaVM *_vm;
int JNI_OnLoad(JavaVM *vm,void *re){
_vm = vm;
return JNI_VERSION_1_2;
}
struct Context{
jobject instance;
};
void* threadTask(void* args){
JNIEnv *env; // native线程附加到 Java 虚拟机
jint i = _vm->AttachCurrentThread(&env,0);
if (i != JNI_OK){
return 0;
}
Context *context = static_cast<Context *>(args);//强转
//获得MainActivity的class对象
jclass cls = env->GetObjectClass(context->instance);
jmethodID updateUI = env->GetMethodID(cls,"updateUI","()V");// 反射获得方法
env->CallVoidMethod(context->instance,updateUI);
delete(context);
context = 0;
//分离线程
_vm->DetachCurrentThread();
return 0;//这里必须要用return,否则会有意想不到的错误
}
extern "C"
JNIEXPORT void JNICALL
Java_com_example_myapplication_MainActivity_testThread(JNIEnv *env, jobject instance) {
pthread_t pid;
Context *context = new Context;
context->instance = env->NewGlobalRef(instance);//将instane赋值给context,这个context是全局的
pthread_create(&pid,0,threadTask,context);
pthread_join(pid,0);//等待子线程执行完毕执行下面的语句输出
LOGI("线程执行完毕");
}
2)分离线程和非分离线程以及线程属性
默认是非分离线程。
分离线程:不能被其它的线程操作join,自己玩自己的;在android中基本不会使用,知道有这个东西就行
extern "C"
JNIEXPORT void JNICALL
Java_com_example_myapplication_MainActivity_testThread(JNIEnv *env, jobject instance) {
pthread_t pid;
Context *context = new Context;
context->instance = env->NewGlobalRef(instance);//将instane赋值给context,这个context是全局的
pthread_attr_t attr;
pthread_attr_init(&attr);//初始化线程属性
pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
pthread_create(&pid,0,threadTask,context);
pthread_attr_destroy(&attr);//销毁线程属性
}
分离线程不能被其他线程等待,pthread_join无效,线程自己玩自己的。
线程具有属性,用 pthread_attr_t 表示:
pthread_attr_t attr;
//初始化 attr中为操作系统实现支持的线程所有属性的默认值
pthread_attr_init(&attr);
pthread_attr_destroy(&attr);
设置是否为分离线程:
//PTHREAD_CREATE_DETACHED 分离
//PTHREAD_CREATE_JOINABLE 非分离
pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
除此之外,还有调度策略与优先级。
设置调度策略:
//返回0 设置成功
pthread_attr_setschedpolicy(&attr, SCHED_FIFO);
SCHED_FIFO
实时调度策略,先到先服务 一旦占用cpu则一直运行。一直运行直到有更高优先级任务到达或自己放弃。
SCHED_RR
实时调度策略,时间轮转系统分配一个时间段,在时间段内执行本线程
设置优先级:
//获得对应策略的最小、最大优先级
int max = sched_get_priority_max(SCHED_FIFO);
int min = sched_get_priority_min(SCHED_FIFO);
sched_param param;
param.sched_priority = max;
pthread_attr_setschedparam(&attr, ¶m);
3)线程安全(互斥)
线程安全就是多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可使用。不会出现数据不一致或者数据污染。
C和C++中的线程安全相当于Java中的synchronized,需要使用锁来操作。
线程互斥是一种特殊的线程同步。实际上,互斥和同步对应着线程间通信发生的两种情况:
(1)当有多个线程访问共享资源而不使资源被破坏时;-->线程安全
(2)当一个线程需要将某个任务已经完成的情况通知另外一个或多个线程时。-->线程同步
#include <jni.h>
#include <string>
#include <pthread.h>
#include <iostream>
#include <queue>
using namespace std;
#include <android/log.h>
#define LOG_TAG "atguigu"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG ,__VA_ARGS__) // 定义LOGD类型
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG ,__VA_ARGS__) // 定义LOGD类型
#define LOGE(...) __android_log_print(ERROR,LOG_TAG ,__VA_ARGS__) // 定义LOGD类型
pthread_mutex_t mut;
queue<int> q;
void* queue_task(void *args){
//锁起来
pthread_mutex_lock(&mut);
if(!q.empty()){
LOGI("获取队列数据:%d\n",q.front());
q.pop();
} else{
LOGI("未获取队列数据\n");
}
pthread_mutex_unlock(&mut);
return 0;
}
extern "C" JNIEXPORT jstring JNICALL
Java_com_example_myapplication_MainActivity_stringFromJNI(
JNIEnv *env,
jobject /* this */) {
std::string hello = "Hello from C++";
//初始化互斥锁
pthread_mutex_init(&mut,0);
for (size_t i = 0; i < 5; i++) {
q.push(i);
}
pthread_t pids[10];
//创建10个线程
for (size_t i = 0; i < 10; i++) {
pthread_create(&pids[i],0,queue_task,0);
}
//销毁互斥锁
pthread_mutex_destroy(&mut);
return env->NewStringUTF(hello.c_str());
}
输出结果:
获取队列数据:0
获取队列数据:1
获取队列数据:2
获取队列数据:3
获取队列数据:4
未获取队列数据
未获取队列数据
未获取队列数据
未获取队列数据
未获取队列数据
4)线程同步
线程同步是指线程之间所具有的一种制约关系,一个线程的执行依赖另一个线程的消息,当它没有得到另一个线程的消息时应等待,直到消息到达时才被唤醒。
#pragma once
#include <queue>
using namespace std;
template <class T> //模板类
class SafeQueue {
public:
SafeQueue() {
pthread_mutex_init(&mutex,0);
pthread_cond_init(&cond, 0);
}
~SafeQueue() {
pthread_mutex_destory(&mutex);
pthread_cond_destory(&cond);
}
void enqueue(T t) {
pthread_mutex_lock(&mutex);
q.push(t);
//发出信号 通知挂起线程
//由系统唤醒一个线程
//pthread_cond_signal(&cond);
// 广播 对应多个消费者的时候 多个线程等待唤醒所有
pthread_cond_broadcast(&cond);
pthread_mutex_unlock(&mutex);
}
int dequeue(T& t) {
pthread_mutex_lock(&mutex);
//可能被意外唤醒 所以while循环
while (q.empty())
{
pthread_cond_wait(&cond, &mutex);
}
t = q.front();
q.pop();
pthread_mutex_unlock(&mutex);
return 1;
}
private:
queue<T> q;
pthread_mutex_t mutex;
pthread_cond_t cond;//条件变量
};
#include "lsn6_example.h"
#include <thread>
#include <pthread.h>
using namespace std;
#include "safe_queue.h"
SafeQueue<int> q;
void *get(void* args) {
while (1) {
int i;
q.dequeue(i);
cout << "消费:"<< i << endl;
}
return 0;
}
void *put(void* args) {
while (1)
{
int i;
cin >> i;
q.enqueue(i);
}
return 0;
}
int main()
{
pthread_t pid1, pid2;
pthread_create(&pid1, 0, get, &q);
pthread_create(&pid2, 0, put, &q);
pthread_join(pid2,0);
system("pause");
return 0;
}
条件变量:当条件满足的时候执行相应操作,不满足时阻塞线程直到满足
网友评论