簡體   English   中英

C#Unity3d中的線程和隊列

[英]Threads and Queues in C# Unity3d

我是Unity3d C#的新手,但不是編程(Java)的人。 我正在嘗試使用Threads,到目前為止,它非常成功,因為它很像Java。 但是我有代碼鎖定,並且試圖找出原因。 DownloadStatus只是一個枚舉。

從ThreadPoolCallback調用以下代碼:

public static Dictionary<int, int> downloading = new Dictionary<int, int>();
...
//Look on the download list
        while (true) {
            foreach (int id in downloading.Keys) {
                lock(downloading){
                    Debug.Log("Thread " + threadIndex + " checking id: "+id);
                    if(downloading [id]==(int)DownloadStatus.WAITING){
                        //This file is waiting
                        downloading [id] = (int)DownloadStatus.DOWLOADING;
                        Debug.Log ("Thread " + threadIndex + " is downloading " + id);
                        //TODO: Actual downloading

                        //Mark as done
                        downloading [id] = (int)DownloadStatus.DONE;
                        Debug.Log ("Thread " + threadIndex + " is done downloading " + id);
                    }else if(downloading [id]==(int)DownloadStatus.DOWLOADING){
                        Debug.Log ("Thread " + threadIndex + " ignoring "+id+" since is already downloading!");
                    }else if(downloading [id]==(int)DownloadStatus.DONE){
                        Debug.Log ("Thread " + threadIndex + " ignoring "+id+" since is already downloaded!");
                    }
                }
            }
            //If you made it here, there's nothing to process
            Debug.Log ("Thread " + threadIndex + " closed!");
            _doneEvent.Set();
            break;
        }

如果我注釋更改字典值的行(例如,下載[id] =(int)DownloadStatus.DOWLOADING;),則可以看到線程遍歷字典中的所有值。 當我不線程不停在那里。 此狀態的唯一目的是防止其他線程嘗試相同的下載。

也許這是實現此目的的錯誤方法。

任何想法?

更新資料

填充下載的代碼:

Retriever.downloading.Add(id,(int)DownloadStatus.WAITING);

其中,Retriever是Thread類的名稱。

根據KeithS的建議,代碼更改為:

public static Dictionary<int, int> downloading = new Dictionary<int, int>();
private readonly object syncObj = new object();
    ...
    //Look on the download list
                foreach (int id in downloading.Keys) {
                    lock(syncObj){
                        Debug.Log("Thread " + threadIndex + " checking id: "+id);
                        if(downloading [id]==(int)DownloadStatus.WAITING){
                            //This file is waiting
                            downloading [id] = (int)DownloadStatus.DOWLOADING;
                            Debug.Log ("Thread " + threadIndex + " is downloading " + id);
                            //TODO: Actual downloading

                            //Mark as done
                            downloading [id] = (int)DownloadStatus.DONE;
                            Debug.Log ("Thread " + threadIndex + " is done downloading " + id);
                        }else if(downloading [id]==(int)DownloadStatus.DOWLOADING){
                            Debug.Log ("Thread " + threadIndex + " ignoring "+id+" since is already downloading!");
                        }else if(downloading [id]==(int)DownloadStatus.DONE){
                            Debug.Log ("Thread " + threadIndex + " ignoring "+id+" since is already downloaded!");
                        }
                    }
                }
                //If you made it here, there's nothing to process
                Debug.Log ("Thread " + threadIndex + " closed!");
                _doneEvent.Set();
                break;
            }

這是輸出(只有2個線程用於測試):

914 files to update!
Thread 1 started...
Thread 2 started...
Thread 1 checking id: 2
Thread 1 is downloading 2
Thread 1 is done downloading 2
Thread 1 closed!

基本上永遠呆在那里。 我以前有過類似的行為。

Unity中最常見的方法是使用協程 盡管它們不是線程,但您將獲得幾乎類似於異步行為的線程(例如,有關Unity的更多信息,請參見Threading in Unity )。

在AltDevBlog上有一篇非常有趣的文章Unity3D協程詳細介紹了該協程,但似乎已刪除。 很高興擁有Internet Archive Wayback機器:
https://web.archive.org/web/20140719082851/http://www.altdev.co/2011/07/07/unity3d-coroutines-in-detail/

使用WWW類可以方便地進行下載。 放在一起:

public void Download (string string url)
{
    StartCoroutine (Download_Coroutine (url));
}
IEnumerator Download_Coroutine (string url)
{
    using (WWW www = WWW.LoadFromCacheOrDownload (bundleUrl, CurrentVersion))
    {
        while (!www.isDone) {
            yield return null;
            // optionally report www.progress to callback
        }
        if (string.IsNullOrEmpty (www.error)) {
            // s. doc for more options to get the content
            AssetBundle bundle = www.assetBundle;
            if (bundle != null) {
                // do something
            }
        }
    }
}

首先,請避免在代碼中還有其他用途的對象上使用lock 最佳實踐是lock專門為此目的創建的對象實例:

private readonly object syncObj = new object();

...

lock(syncObj)
{
 ...

第二, while(true)語句在當前實現時是多余的。 每個工作人員將遍歷整個下載集合,直到找到“正在等待”的下載為止,它將處理該下載,然后繼續進行下一個記錄。 一旦每個工作人員到達集合的末尾,它將在循環的底部執行代碼,該代碼將中斷它,甚至不會循環一次。

現在,僅基於此代碼(以及用於填充downloading的看不見的代碼),您可能對該集合有錯誤的抽象。 多線程代碼的目的是並行地分割和征服許多下載。 因此,為什么不將它們作為Queue<Tuple<int,int>> (或者甚至更好的是ConcurrentQueue<Tuple<int,int>> )加載,並讓您的工作線程從隊列中拉出項目,直到沒有還有嗎? 這樣,它是集合,而不是工人或工作項,請確保只有一個工人曾經擁有過特定的工作項。

工作人員將從隊列中取出項目(為此目的,對“ TryTake()”方法的訪問已同步,因此一次只能使一個工作人員出隊),下載它代表的文件,然后完成操作,它可以將項目放置在ConcurrentDictionary中以指示它已完成,然后返回Queue進行更多操作,直到沒有更多為止。 如果下載出現錯誤,工作人員應抓住該錯誤並將其放回隊列中,以便其他工作人員可以嘗試,或將其放入錯誤集合中(也許基於多次重試下載次數或性質錯誤)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM