50元求教一个多线程教程布局

&nbsp>&nbsp
&nbsp>&nbsp
&nbsp>&nbsp
多线程实现多图片下载1
摘要:展示效果如下:&大家可以看到这个界面很简单,其实就是UITableView的布局,但是难点是在于如何从网上下载这些图片,下载之后应如何进行存储!&我们一步一步进行解析,先从单线程(主线程)进行多图片下载我们布局上的文字及图片的地址从plist文件中进行读取根据结构,我们自定义一个数据模型文件DDZApp.h#import&Foundation/Foundation.h&@interfaceDDZApp:NSObject//图标@property
展示效果如下:
大家可以看到这个界面很简单,其实就是UITableView的布局,
但是难点是在于如何从网上下载这些图片,下载之后应如何进行存储!
我们一步一步进行解析,先从单线程(主线程)进行多图片下载
我们布局上的文字及图片的地址从plist文件中进行读取
根据结构,我们自定义一个数据模型文件
#import &Foundation/Foundation.h&@interface DDZApp : NSObject//图标@property (nonatomic,strong) NSString *//名字@property (nonatomic,strong) NSString *//下载量@property (nonatomic,strong) NSString *+ (instancetype)appWithDict:(NSDictionary *)@end
#import &DDZApp.h&@implementation DDZApp+ (instancetype)appWithDict:(NSDictionary *)dict {
DDZApp *app = [[self alloc] init];
[app setValuesForKeysWithDictionary:dict];
return}@end
以下的都是视图控制器中的代码
ViewController.m
@interface ViewController ()//所有数据@property (nonatomic,strong)NSArray *//内存缓存图片@property (nonatomic,strong)NSMutableDictionary *imgC@end
第一个属性用于存储读取plist文件中的内容,设置为属性保存起来,就可以不用重复读取
第二个属性用于保存从网上下载下来的图片,也是为了不用重复读取
@implementation ViewController//读取数据- (NSArray *)apps {
if (!_apps) {
//从plist文件中读取数据
NSArray *dictArray = [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@&apps.plist& ofType:nil]];
NSMutableArray *appArray = [NSMutableArray array];
for (NSDictionary *dict in dictArray) {
[appArray addObject:[DDZApp appWithDict:dict]];
_apps = appA
return _}//缓存图片- (NSMutableDictionary *)imgCache {
if (!_imgCache) {
_imgCache = [NSMutableDictionary dictionary];
return _imgC}
这两个方法都是为了初始化刚才的两个属性
#pragma mark - 数据源方法- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.apps.}- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *ID = @&app&;
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
DDZApp *app = self.apps[indexPath.row];
cell.textLabel.text = app.
cell.detailTextLabel.text = app.
//先从内存中取出图片
UIImage *image = self.imgCache[app.icon];
if (image) {
cell.imageView.image =
//内存中没有图片
//将图片文件数据写入到沙盒中
NSString *cachesPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];
//获得文件名
NSString *filename = [app.icon lastPathComponent];
//计算出文件的全路径
NSString *file = [cachesPath stringByAppendingPathComponent:filename];
//加载沙盒的文件数据
NSData *data = [NSData dataWithContentsOfFile:file];
//判断沙盒中是否有图片
if (data) {
//直接加载沙盒中图片
cell.imageView.image = [UIImage imageWithData:data];
//存到字典(内存)中
self.imgCache[app.icon] = cell.imageView.
//下载图片
data = [NSData dataWithContentsOfURL:[NSURL URLWithString:app.icon]];
cell.imageView.image = [UIImage imageWithData:data];
//存到内存中
self.imgCache[app.icon] = cell.imageView.
//将图片数据写入到沙盒中
[data writeToFile:file atomically:YES];
这两个方法是UITableView必须要实现的方法
第一个是返回数据量,没什么好说的
第二个是绑定数据
具体的流程看下图
以上是的内容,更多
的内容,请您使用右上方搜索功能获取相关信息。
若你要投稿、删除文章请联系邮箱:zixun-group@service.aliyun.com,工作人员会在五个工作日内给你回复。
云服务器 ECS
可弹性伸缩、安全稳定、简单易用
&40.8元/月起
预测未发生的攻击
&24元/月起
为您提供0门槛上云实践机会
你可能还喜欢
你可能感兴趣
阿里云教程中心为您免费提供
多线程实现多图片下载1相关信息,包括
的信息,所有多线程实现多图片下载1相关内容均不代表阿里云的意见!投稿删除文章请联系邮箱:zixun-group@service.aliyun.com,工作人员会在五个工作日内答复
售前咨询热线
支持与服务
资源和社区
关注阿里云
International查看: 299|回复: 2
50元求教一个多线程布局
阅读权限140
签到天数:2 天结帖率: (4/9)
交易币定制软件
是否要源码:
要求完成日期:
联系下单方
定制要求见帖子下方↓
& &50元求一个多线程post布局&&会的来~!!!!!!!!!!!
拒绝任何人以任何形式在本论坛发表与中华人民共和国法律相抵触的言论,本站内容均为会员发表,并不代表精易立场!
揭阳精易科技有限公司申明:我公司所有的培训课程版权归精易所有,任何人以任何方式翻录、盗版、破解本站培训课程,我们必将通过法律途径解决!
公司简介:揭阳市揭东区精易科技有限公司致力于易语言教学培训/易语言学习交流社区的建设与软件开发,多年来为中小企业编写过许许多多各式软件,并把多年积累的开发经验逐步录制成视频课程供学员学习,让学员全面系统化学习易语言编程,少走弯路,减少对相关技术的研究与摸索时间,从而加快了学习进度!
防范网络诈骗,远离网络犯罪
违法和不良信息举报电话,QQ: ,邮箱:@b.qq.com
Powered by
X3.2 揭阳市揭东区精易科技有限公司
粤公网安备 25阅读我的最新系列文章:
多线程开发(中)
第3节 Handler
多个线程之间除了有“执行的同步”关系,还有“数据的共享”关系,以及“功能的委托”关系。
例如之前提到的视频信息显示到列表上,
委托数据查询功能:主线程启动一个工作线程thread-查询视频信息;
委托数据的界面更新功能:工作线程查询完成后,因为不能够修改界面元素,所以必须将结果通知到主线程,委托主线程将得到的结果显示到界面上。
为此,Android SDK提供了Handler帮助各个不同的线程之间传递数据或委托功能处理。
3.1 Thread、Looper与Handler的关系
一个线程创建并start以后,就会开始执行它Runnable中的run()方法。如果要这个线程一直运行而不退出的话,就要在里面设置一个循环,
Runnable runnable = new Runnable() {
public void run() {
while(xxx)
Looper可以为Thread创建这样一个循环的环境,
public void run() {
Looper.prepare();
Looper.loop();
Looper具有一个消息队列,可以存放任何线程(包括它自己)给自己布置的任务。这些任务被一条一条的放在队列当中,在loop()函数中被取出来-执行,然后又取出来-执行,周而复始,永不停止。
public static void loop() {
for (;;) {
Message msg = queue.next();
msg.target.dispatchMessage(msg);
msg.recycleUnchecked();
Handler是和Looper相关联的,通过Handler,任何线程可以把需要完成的任务放到Looper的消息队列里面。
Thread就好比一条产线,Looper中的消息队列就是这个流水线上的传送带,带子上分成了很多格,每一格放要处理的原料和处理这些原料的工人(原料和工人打包成了一个Message)。等轮到格子上的工人时,工人才能开始处理格子里放的原料。Handler就像是一个转运工具,提供给别的模块使用。这个工具可以把原料和工人放到产线的传送带上。
注意,一条产线只有一条传送带(Looper);但可以有多个为同一条产线提供转运服务的转运工具(Handler)。
所以使用这种产线的流程是,
创建一条产线A;
在这条产线A上创建一条传送带;
当别的模块B要向这条产线布置任务的时候,就要创建在产线A上工作的工人;
B要告诉工人携带哪些原料,怎么处理这些原料;
等轮到产线A上对应的格子被处理的时候,上面的工人就开始操作了;
3.2 Handler的使用
Handler最常见的使用场景是:
为了进行耗时操作,主线程创建一个工作线程完成耗时操作;
工作线程开始耗时工作,时不时向主线程发送消息,告知当前完成的状态;
主线程根据工作线程的报告,更新界面元素,展示工作进度;
为了实现这样的功能,我们首先需要知道,
每个Activity都是运行在程序的主线程当中的;
只有主线程能修改界面元素,其他线程修改的话,应用会崩溃;
主线程在创建之后,系统已经为它设置了Looper,主线程已经有了消息处理的队列(生产流水线和流水线上的格子);
用Handler处理这种场景时,我们有2种方式。
3.2.1 使用sendMessage()
创建一个能向主线程消息队列中布置任务的Handler;如果创建时直接new Handler(),说明这个Handler是放在主线程的消息队列中的“工人”;
重写Handler的handleMessage()函数;
在handleMessage()函数中,根据msg.what的不同,进行对应的处理;
private Handler mHandler = new Handler()
public void handleMessage(Message msg) {
switch(msg.what)
case MSG_XXX:
case ......
super.handleMessage(msg);
任何想要布置任务的线程只需要利用Handler的sendMessage()函数,就能把任务放到主线程的消息队列中,
Runnable runnable = new Runnable() {
public void run() {
while(!stop)
Message msg = mHandler.obtainMessage(MSG_XXX);
mHandler.sendMessage(msg);
Thread work = new Thread(runnable);
work.start();
这样主线程的消息队列中就有了这个新任务。等到这个消息被处理的时候,主线就可以根据参数修改界面元素了。
Message msg = mHandler.obtainMessage(MSG_XXX);
mHandler.sendMessage(msg);
也可以使用
mHandler.obtainMessage(MSG_XXX).sendToTarget();
这里使用的Message就携带了“工人”和“原料”,Message通过Handler对象获取,
Message msg = mHandler.obtainMessage(what);
Message msg = mHandler.obtainMessage(what, object);
Message msg = mHandler.obtainMessage(what, arg1, arg2, object);
发送Message可以直接发送,也可以延时发送,
mHandler.sendMessage(msg);
mHandler.sendDelayedMessage(msg,1000)
3.2.2 使用post()
创建一个能向主线程消息队列中布置任务的Handler;如果创建时直接new Handler(),说明这个Handler是放在主线程的消息队列中的工人;
Handler mHandler = new Handler();
在工作线程中使用Handler的post()方法,里面的Runnable就是在Handler所服务线程中运行;
Runnable mHandlerR
Runnable runnable = new Runnable() {
public void run() {
while(!stop)
mHandlerRunnable = new Runnable() {
public void run() {
mHandler.post(mHandlerRunnable);
Thread work = new Thread(runnable);
work.start();
还可以使用
mHandler.postDelayed(mHandlerRunnable, 1000);
3.3 Handler任务的移除
大多数情况下,当Activity退出以后,需要将它布置给主线程的任务给移除掉。如果不移除,可能会遇到大麻烦。可以想象一下,
工作线程通过Handler给主线程布置了一个任务-根据一个参数修改界面显示,此时这个任务已经放到了主线程的任务队列里面;
用户突然退出了这个Activity,Activity被销毁了,但是主线程是不会退出的(Activity只是主线程上长的一个果子,果子被摘了,但是不会影响树的存在);
主线程的任务队列依次执行到了工作线程布置的任务,任务要求更新Activity上的界面元素,但是这个Activity已经被销毁了;
这时,程序的行为表现有可能和你希望的不同,出现一些与期望不符的现象,严重时,可能会造成程序崩溃。所以当一个Activity退出销毁的时候,一定要把相关的任务移除。这样做还有助于避免内存泄露。
3.3.1 使用removeMessages()
通过它移除任务队列中所有特定Message,
mHandler.removeMessages(MSG_XXX);
3.3.2 使用removeCallbacks()
通过它移除任务队列中所有特定Runnable,
mHandler.removeCallbacks(mHandlerRunnable);
这里的mHandlerRunnable就是之前post()方式使用的mHandlerRunnable,所以在使用post()方式的时候,我们要把Runnable的引用保存起来,以便以后的移除操作。
/*******************************************************************/
* 版权声明
* 本教程只在和发布,其他网站出现本教程均属侵权。
*另外,我们还推出了Arduino智能硬件相关的教程,您可以在我们的网店中购买相关硬件。同时也感谢大家对我们这些码农的支持。
*最后再次感谢各位读者对安豆的支持,谢谢:)
/*******************************************************************/
没有更多推荐了,
不良信息举报
举报内容:
多线程开发(中)
举报原因:
原文地址:
原因补充:
最多只允许输入30个字
加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!查看: 2758|回复: 9
求教一个多线程循环的问题,BLOCK和THREAD能分别作两层嵌套循环吗?
论坛徽章:0
我的意图就是各个BLOCK做最外层循环, 然后每个BLOCK里的线程做里层循环 不知道这样实现可行否, 因为看到好多例程都是直接用BLOCK数乘上线程数得到总线程大小后做一次循环完成. 现在我按我下面代码的方式实现,结果行不通,计算出的结果不对&&我对各个BLOCK以及各线程运行机制还不太明白, 不知道要想实现我的意图该如何去改写代码
#define BLOCK_NUM 16
#define THREAD_NUM 256
__global__ void kernel(.......){
int tid =&&threadIdx.x;
int bid =&&blockIdx.x;
for(int i = i & i++)
.............
& & & & for(int j = j & j++)//或者还是for(int j = j & j += THREAD_NUM)
& & & & & & & & ........
论坛徽章:21
回复 #1 rapjan 的帖子
要注意乱序执行和数据依赖之间的冲突。这个比较难啊!写并行程序基本上都要解决这个问题,呵呵
论坛徽章:0
乱序执行这个不太明白是怎么回事 是指不同BLOCK里的线程可能会有冲突吗
论坛徽章:21
回复 #3 rapjan 的帖子
不只是不同block,同一block也有可能,呵呵!一般并行编程要注意的就是读写冲突和数据依赖。呵呵!
论坛徽章:20
LZ的程序有问题,或者说按照LZ想要去实现的意思的意义下不对,你的那外层和内层循环在每个BLOCK中的迭代次数都构成了一个”上三角形“,而且这2层循环也只是在单个线程内,不具有全局循环展开的意义
论坛徽章:21
回复 #5 cyrosly 的帖子
cyrosly大牛的话更清晰了,本质上还是因为有数据依赖,或顺序相关,也就是不能乱序执行。
论坛徽章:0
谢谢楼上两位了&&有点茅塞顿开的感觉了....
论坛徽章:0
楼主是想用自己写的循环来执行CUDA调用程序的功能,其实这功能本身已经很好用了,调用XXkernel&&&栅格数,块数,线程数&&&就能形成一个3层的循环,比如kernel&&&2,10,256&&& 就是把这个程序调用256次为一个块,然后中间的块数减1,再调用256次,以此类推。。。。每一个线程都有自己的ID就是标号,这程序调用执行的时候,会自动用一个闲置的GPU执行一个线程,所有GPU都用上了,都忙起来了就等待,有一个忙完了就继续调用,所以具体哪个GPU处理哪一个ID的线程是不一定的,没有顺序的,一整个块忙完了可以要求等待所有GPU都空闲了再继续,以免数据冲突,比如需要上一步的结果什么的。
这样在调用XXkernel&&&2,10,256&&&之前,可以在XXkernel() {。。。。 }&&程序里面用FOR 之类的循环语句,这样是一层有序循环,与此同时线程是不断被执行的,虽然是乱序,但已经执行过的线程是不会重复的,所以可以利用线程ID来形成另一层乱序循环,然后利用块的ID,以些类推。。。说到这里我自己都快晕了& & CUDA编程里面N多这样的例子,整天看的我头晕
论坛徽章:0
上面用的数字,代号,甚至程序名都是随便起的,仅供参考& &说的哪里不对也希望高手可以补充, 我现在还卡在某一初级阶段,ID号的计算和利用ID取数组值都看的挺晕,pitch 和 ptr 更晕,希望高手们能指点一下
论坛徽章:21
回复 #9 chris1244 的帖子
你应该去写点代码,而不是看书,呵呵!
itpub.net All Right Reserved. 北京盛拓优讯信息技术有限公司版权所有    
 北京市公安局海淀分局网监中心备案编号:10 广播电视节目制作经营许可证:编号(京)字第1149号新手园地& & & 硬件问题Linux系统管理Linux网络问题Linux环境编程Linux桌面系统国产LinuxBSD& & & BSD文档中心AIX& & & 新手入门& & & AIX文档中心& & & 资源下载& & & Power高级应用& & & IBM存储AS400Solaris& & & Solaris文档中心HP-UX& & & HP文档中心SCO UNIX& & & SCO文档中心互操作专区IRIXTru64 UNIXMac OS X门户网站运维集群和高可用服务器应用监控和防护虚拟化技术架构设计行业应用和管理服务器及硬件技术& & & 服务器资源下载云计算& & & 云计算文档中心& & & 云计算业界& & & 云计算资源下载存储备份& & & 存储文档中心& & & 存储业界& & & 存储资源下载& & & Symantec技术交流区安全技术网络技术& & & 网络技术文档中心C/C++& & & GUI编程& & & Functional编程内核源码& & & 内核问题移动开发& & & 移动开发技术资料ShellPerlJava& & & Java文档中心PHP& & & php文档中心Python& & & Python文档中心RubyCPU与编译器嵌入式开发驱动开发Web开发VoIP开发技术MySQL& & & MySQL文档中心SybaseOraclePostgreSQLDB2Informix数据仓库与数据挖掘NoSQL技术IT业界新闻与评论IT职业生涯& & & 猎头招聘IT图书与评论& & & CU技术图书大系& & & Linux书友会二手交易下载共享Linux文档专区IT培训与认证& & & 培训交流& & & 认证培训清茶斋投资理财运动地带快乐数码摄影& & & 摄影器材& & & 摄影比赛专区IT爱车族旅游天下站务交流版主会议室博客SNS站务交流区CU活动专区& & & Power活动专区& & & 拍卖交流区频道交流区
丰衣足食, 积分 578, 距离下一级还需 422 积分
论坛徽章:2
主进程的某一个函数会接收很多参数,它把这些参数组成一个结构体,然后启动一个线程并把结构体地址传给线程的启动函数。接着线程去做一下处理(很长时间),而调用函数会返回。我想问得是,当调用函数返回是,参数结构体不就应该释放了吗?线程这时候用的参数结构体是不是就成为了野指针?
例子如下:void * fun2(void * arg){
& & //实现略,需要很从时间处理
}
int fun1(char *arg1,char *arg2,...(很多参数)){
& & struct arg_t *a = NULL;
& & a-&arg1 = arg1;
& & ...
& & //参数都赋值到结构体中
& & pthread_
& & pthread_create(&tid, NULL, fun2, (void *)a);
& & return 0;
}复制代码请问,当fun1退出的时候,结构体a是不是就成了野指针了?这个时候,fun2使用参数的时候会有风险吗?谢谢各位!
大富大贵, 积分 11624, 距离下一级还需 8376 积分
论坛徽章:250
fun1&&new&&a
fun2 用完 delete a
丰衣足食, 积分 578, 距离下一级还需 422 积分
论坛徽章:2
& & fun1 显示的 new a
& & fun2 是隐示的 delete a吗?
& & 谢谢回复 。
大富大贵, 积分 11624, 距离下一级还需 8376 积分
论坛徽章:250
love5783 发表于
回复 2# hellioncu
自己调用delete a
丰衣足食, 积分 578, 距离下一级还需 422 积分
论坛徽章:2
& & 明白了,非常感谢。
白手起家, 积分 27, 距离下一级还需 173 积分
论坛徽章:0
看来是没有明白什么是在函数调用的时候参数是用值传递的
家境小康, 积分 1162, 距离下一级还需 838 积分
论坛徽章:0
一般而言,鼓励的是 谁NEW谁DELETE吧。。。。。
是否有更好的解决方案呢。。
struct arg_t *a = NULL;
& & a-&arg1 = arg1;
这。。。。不挂?。。。当然,当伪代码看,未尝不可。。
大富大贵, 积分 11624, 距离下一级还需 8376 积分
论坛徽章:250
大众推荐 发表于
一般而言,鼓励的是 谁NEW谁DELETE吧。。。。。
是否有更好的解决方案呢。。
这里的目的是指出问题的根本,在只提供这些信息的情况下,没法提更好的解决方案,也不容易说清楚
北京盛拓优讯信息技术有限公司. 版权所有 京ICP备号 北京市公安局海淀分局网监中心备案编号:22
广播电视节目制作经营许可证(京) 字第1234号
中国互联网协会会员&&联系我们:
感谢所有关心和支持过ChinaUnix的朋友们
转载本站内容请注明原作者名及出处}

我要回帖

更多关于 c 多线程教程 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信