延迟加载和多线程

时间:2012-01-31 20:37:00

标签: c# ios multithreading uiscrollview xamarin.ios

我正在尝试在monotouch中使用iCarousel库。我成功地移植了库,一切都运行得很好但是如果你输入太多带有图像的UIImageViews应用程序会崩溃,这是正常的,因为iCarousel就像一个UIScrollView。

我必须以某种方式从辅助线程使用延迟加载系统,并且一次只显示3-4张图像,但我不知道如何使这项工作顺利进行。

此时,我在iCarousel代表中设置了这个:

bool threadsAlive = true; 

public cDelegate() 
{ 
    ThreadPool.QueueUserWorkItem( delegate { refresh_visible(); } ); 
} 

public override void DidScroll (iCarousel carousel) 
{ 
        scrolling = true; 
} 

public override void DidEndScrollingAnimation (iCarousel carousel) 
{ 
        scrolling = false;  
        //show images that are currently on the screen 
        ThreadPool.QueueUserWorkItem( delegate { ShowCurrent();         } ); 
        //hides images that are not on the screen         
        ThreadPool.QueueUserWorkItem( delegate { hideInvisibleImages(); } ); 
} 

void refresh_visible() 
{ 
        while( threadsAlive ) 
        { 
                while( scrolling ) 
                { 
                        ShowCurrent(); 
                } 
        } 
} 

void refresh_hidden() 
{ 
        while( threadsAlive ) 
        { 
                while( scrolling ) 
                { 
                        hideInvisibleImages(); 
                } 
        } 
} 

public void ShowCurrent() 
{ 
        var          ds = _carousel.DataSource as cDataSource; 
        var left_index  = _carousel.CurrentItemIndex - 1; 
        var right_index = _carousel.CurrentItemIndex + 2; 
        if( left_index  <  0 ) left_index  = 0; 
        if( right_index >= ds.Lista.Count ) right_index = ds.Lista.Count - 1; 
        // 
        for( var i = left_index; i < right_index ; i++ ) 
        { 
                var img = ds.Lista[i]; 
                if( img.Image == null ) 
                { 
                        BeginInvokeOnMainThread( delegate{ 
                                img.Image = UIImage.FromFile( img.UserObject.ToString() ); 
                        }); 
                } 
        } 
} 


void hideInvisibleImages() 
{ 
        Console.WriteLine("ascund!"); 
        var          ds = _carousel.DataSource as cDataSource; 
        var left_index  = _carousel.CurrentItemIndex - 1; 
        var right_index = _carousel.CurrentItemIndex + 2; 
        if( left_index  <  0 ) left_index  = 0; 
        if( right_index >= ds.Lista.Count ) right_index = ds.Lista.Count - 1; 
        // 
        for( var i=0; i<left_index; i++ ) 
        { 
                var img   = ds.Lista[i]; 
                if( img.Image != null ) 
                { 
                        img.Image.Dispose(); 
                        img.Image = null; 
                } 
        } 
        for( var i=right_index; i<ds.Lista.Count; i++ ) 
        { 
                var img   = ds.Lista[i]; 
                if( img.Image != null ) 
                { 
                        img.Image.Dispose(); 
                        img.Image = null; 
                } 
        } 
} 

代码实际上非常简单:主线程只显示当前索引左侧的1个图像,提前显示两个图像,另一个清除所有其他图像的线程隐藏它们。

它正常工作,内存还可以,但它在设备上并不流畅,当我滚动时它会“挂起”一点。还有另一种方法吗?或许我应该改变算法?

2 个答案:

答案 0 :(得分:2)

您正在拥有一个不允许CPU进入任何其他线程/进程的循环,并且会导致非常高的CPU利用率。滚动时会使它挂起。

尝试在refresh_visible方法中使用Thread.Sleep(1)或小睡眠时间。

答案 1 :(得分:2)

我对你的滚动工作方式有些困惑,但我认为下面的代码可以为你解决这个问题提供一个不错的起点。

正如我们在评论部分指出的那样,你会不断旋转并尽快刷新图像(如果我理解你的代码正确的话):

void refresh_visible() 
{ 
        while( threadsAlive ) 
        { 
                while( scrolling ) 
                { 
                        ShowCurrent(); 
                } 
        } 
} 

void refresh_hidden() 
{ 
        while( threadsAlive ) 
        { 
                while( scrolling ) 
                { 
                        hideInvisibleImages(); 
                } 
        } 
} 

似乎无论滚动还是适当的刷新率都可能是个好主意。您应该实现一个介于每秒24到30帧之间的刷新率。

这样的事情可能是有序的:

using System.Threading;

class YourClass
{
    // Tick every 42 millisecond or about 24 times per second
    private readonly int _refreshRate = 42;

    private volatile bool _scrolling;
    private Timer _timer;
    YourClass()
    {
        _timer = new Timer(TimerTick, null, 0, _refreshRate);
    }

    public void TimerTick(object state)
    {
        if (_scrolling)
        {
            ShowCurrent();
            HideInvisibleImages();
        }
    }

    void ShowCurrent()
    {
        //...
    }

    void HideInvisibleImages()
    {
        //...
    }
}

请注意,如果您正在创建和销毁大量YourClass个实例,那么在完成后,您还应该处理Timer个对象。 Timer的委托将保留对YourClass实例的引用,它将阻止它被垃圾回收。