Unity Eğitim
Dersler
Forum Sınav Merkezi Premium 💎
C# Dersleri

Unity'de C# Task ve CancellationToken ile Asenkron İşlemler

Paylaşan: Unity Eğitim 06 October 2025 9 dakika okuma 59 görüntülenme

Unity'de C# Task ve CancellationToken ile Asenkron İşlemler

Oyun geliştirme süreçlerinde, özellikle Unity gibi güçlü platformlarda, uzun süren veya yoğun kaynak tüketen işlemlerle karşılaşmak kaçınılmazdır. Dosya okuma/yazma, ağ istekleri, karmaşık algoritmalar veya büyük veri işleme gibi görevler, ana iş parçacığını (main thread) bloke ederek oyunun donmasına veya kullanıcı arayüzünün (UI) tepkisiz kalmasına neden olabilir. Bu durum, oyuncu deneyimini olumsuz etkileyen en yaygın sorunlardan biridir. İşte bu noktada, C# dilinin sunduğu Task ve CancellationToken yapıları devreye girer. Bu makalede, Unity projelerinizde asenkron programlamayı nasıl etkili bir şekilde kullanacağınızı, performans sorunlarını nasıl çözeceğinizi ve daha akıcı bir oyun deneyimi sunacağınızı detaylı bir şekilde inceleyeceğiz. Özellikle C# Task CancellationToken ikilisinin gücünü keşfederek, projelerinizi bir üst seviyeye taşıyacağız.

C# Task Nedir ve Neden Önemlidir?

Task, C#'ta asenkron programlamanın temel yapı taşlarından biridir. Bir işlemi ana iş parçacığından bağımsız olarak, arka planda çalıştırmak için kullanılır. Bu sayede, uzun süren işlemler devam ederken bile uygulamanızın UI'ı donmaz ve tepkisiz kalmaz. async ve await anahtar kelimeleriyle birlikte kullanıldığında, asenkron kod yazmak senkron kod yazmak kadar kolay ve okunabilir hale gelir.

Unity'de Task kullanmanın en büyük avantajı, ana iş parçacığının (genellikle UI ve oyun mantığının çalıştığı tek iş parçacığı) bloke edilmesini engellemektir. Örneğin, internetten büyük bir dosya indirme işlemi sırasında oyununuzun donmasını istemezsiniz. Task kullanarak bu indirme işlemini arka plana atabilir, tamamlandığında ise sonucu ana iş parçacığına geri getirebilirsiniz. Ancak unutulmamalıdır ki, Unity'nin birçok API'si (GameObject'lere erişim, UI güncellemeleri vb.) sadece ana iş parçacığında çağrılabilir. Bu yüzden Task içinden doğrudan bu API'lere erişmek hataya yol açacaktır. Sonuçları ana iş parçacığına güvenli bir şekilde aktarmak için özel yöntemler (örneğin, bir kuyruk veya UnitySynchronizationContext kullanmak) gereklidir.

using System.Threading.Tasks;
using UnityEngine;

public class TaskExample : MonoBehaviour
{
    public async void StartLongRunningTask()
    {
        Debug.Log("Uzun süren işlem başlatılıyor...");
        // Arka planda çalışacak asenkron bir Task tanımla
        await Task.Run(() =>
        {
            // Bu kod ana iş parçacığında çalışmaz
            for (int i = 0; i < 5; i++)
            {
                Task.Delay(1000).Wait(); // İşlemi simüle etmek için bekle
                Debug.Log($"İşlem devam ediyor... {i + 1}/5");
            }
            Debug.Log("Arka plan işlemi tamamlandı.");
        });

        Debug.Log("Görev tamamlandı, ana iş parçacığına dönüldü.");
    }

    void Start()
    {
        StartLongRunningTask();
        Debug.Log("Oyun hala akıcı bir şekilde çalışıyor...");
    }
}

CancellationToken Nedir ve Neden Kullanmalıyız?

Uzun süren bir Task başlattınız, ancak belirli bir koşul altında bu işlemi durdurmanız veya iptal etmeniz gerekti. Örneğin, kullanıcı oyundan çıktı, sahne değişti veya işlem artık gerekli değil. Bu gibi durumlarda, Task'ın kendi kendine tamamlanmasını beklemek kaynak israfına (CPU döngüsü, bellek) yol açabilir ve istenmeyen davranışlara neden olabilir. İşte burada CancellationToken devreye girer.

CancellationToken, asenkron işlemleri güvenli ve kontrollü bir şekilde iptal etmek için kullanılan bir yapıdır. Bir CancellationTokenSource nesnesi oluşturulur, bu nesneden bir CancellationToken elde edilir ve bu token asenkron göreve (Task'a) iletilir. Görev içerisinde belirli aralıklarla bu token'ın iptal edilip edilmediği kontrol edilir. Eğer token iptal edilmişse, görev kendi kendini temizleyerek gracefully (hatasız bir şekilde) sonlandırılır.

using System.Threading;
using System.Threading.Tasks;
using UnityEngine;

public class CancellationTokenExample : MonoBehaviour
{
    private CancellationTokenSource _cancellationTokenSource;

    public async void StartCancellableTask()
    {
        _cancellationTokenSource = new CancellationTokenSource();
        CancellationToken token = _cancellationTokenSource.Token;

        Debug.Log("İptal edilebilir görev başlatıldı...");

        try
        {
            await Task.Run(() =>
            {
                for (int i = 0; i < 10; i++)
                {
                    token.ThrowIfCancellationRequested(); // İptal isteği varsa istisna fırlatır
                    Task.Delay(500).Wait();
                    Debug.Log($"Görev devam ediyor... {i + 1}/10");
                }
            }, token); // Task'a token'ı geçirmeyi unutmayın

            Debug.Log("Görev başarıyla tamamlandı.");
        }
        catch (OperationCanceledException)
        {
            Debug.Log("Görev iptal edildi.");
        }
        finally
        {
            _cancellationTokenSource.Dispose(); // Kaynakları temizle
            _cancellationTokenSource = null;
        }
    }

    public void CancelTask()
    {
        if (_cancellationTokenSource != null)
        {
            _cancellationTokenSource.Cancel(); // İptal isteği gönder
            Debug.Log("İptal isteği gönderildi.");
        }
    }

    void OnDestroy()
    {
        // Oyun objesi yok edildiğinde görevi iptal et ve kaynakları temizle
        _cancellationTokenSource?.Cancel();
        _cancellationTokenSource?.Dispose();
    }

    void Start()
    {
        StartCancellableTask();
        // Birkaç saniye sonra iptal etmek için bir buton veya başka bir olay kullanabilirsiniz.
        // Invoke("CancelTask", 3f); // Örnek olarak 3 saniye sonra iptal et
    }
}

C# Task ve CancellationToken Birlikte Kullanım İpuçları

C# Task CancellationToken ikilisini Unity projelerinizde verimli bir şekilde kullanmak için bazı önemli ipuçları:

1. Unity Ana İş Parçacığı Kısıtlamalarına Dikkat Edin

Daha önce de belirtildiği gibi, Unity'nin birçok API'si (örneğin, GameObject.Find, GetComponent, Instantiate, UI güncellemeleri) sadece ana iş parçacığında çağrılmalıdır. Arka planda çalışan bir Task içinden bu API'lere doğrudan erişmeye çalışmak, çalışma zamanı hatalarına veya kararsız davranışlara yol açar. Eğer arka planda bir işlem sonucunda Unity objeleriyle etkileşim kurmanız gerekiyorsa, bu etkileşimi ana iş parçacığına geri göndermelisiniz. Bunun için bir Queue yapısı kullanarak ana iş parçacığında çalışan bir Update metodunda kuyruktaki işlemleri işleyebilirsiniz veya daha gelişmiş bir çözüm olarak UnitySynchronizationContext'i kullanan bir MainThreadDispatcher yapısı oluşturabilirsiniz.

2. Uzun Süren Hesaplamalar ve G/Ç İşlemleri İçin Task.Run() Kullanın

Büyük veri setlerinin işlenmesi, karmaşık yol bulma algoritmaları, ağdan veri indirme veya diske dosya yazma gibi işlemler genellikle ana iş parçacığını bloke etme potansiyeline sahiptir. Bu tür CPU yoğun veya G/Ç (giriş/çıkış) yoğun işlemleri Task.Run(() => { ... }) ile arka plana atarak ana iş parçacığının akıcı kalmasını sağlayabilirsiniz. Örneğin, bir seviyeyi yüklerken veya büyük bir texture'ı sıkıştırırken kullanıcı arayüzünün donmasını engellemek için bu yaklaşım idealdir.

3. Kaynak Temizliği ve İptal Mekanizmasını Doğru Yönetin

Bir Task başlattığınızda, bu Task'ın ömrünü doğru bir şekilde yönetmek önemlidir. Özellikle bir MonoBehaviour sınıfı içinde başlatılan asenkron görevler, obje yok edildiğinde (OnDestroy) veya devre dışı bırakıldığında (OnDisable) iptal edilmelidir. Aksi takdirde, obje yok olsa bile arka plandaki görev çalışmaya devam edebilir, bu da bellek sızıntılarına veya NullReferenceException gibi hatalara yol açabilir. CancellationTokenSource.Cancel() metodunu çağırarak görevi iptal edin ve ardından CancellationTokenSource.Dispose() ile kaynakları temizleyin. using ifadesi, CancellationTokenSource gibi IDisposable arayüzünü uygulayan nesnelerin otomatik olarak temizlenmesini sağlar.

Yaygın Hatalar ve Çözümleri

1. Task İçinden Doğrudan Unity API'lerine Erişmek

Hata: Arka planda çalışan bir Task içinden transform.position = ... veya Debug.Log() gibi Unity API'lerini çağırmak. Debug.Log genellikle çalışsa da, çoğu diğer Unity API'si ana iş parçacığı dışında çağrıldığında hata verir.

Çözüm: Task'ın sonucunu ana iş parçacığına geri döndürün ve Unity API'leriyle etkileşimi ana iş parçacığında gerçekleştirin. Basit bir örnek olarak, bir metodu ana iş parçacığında çağırmak için UnityMainThreadDispatcher gibi bir yapı kullanabilir veya bir Queue'ya eylem ekleyip Update() metodunda işleyebilirsiniz.

// Örnek: Ana iş parçacığına dönmek için basit bir yöntem
public static class MainThreadDispatcher
{
    private static Queue<Action> _actions = new Queue<Action>();
    public static void Enqueue(Action action)
    {
        lock (_actions) _actions.Enqueue(action);
    }

    // Bu metot bir MonoBehaviour'ın Update() veya LateUpdate() metodundan çağrılmalı
    public static void ProcessQueue()
    {
        while (_actions.Count > 0)
        {
            Action action;
            lock (_actions) action = _actions.Dequeue();
            action?.Invoke();
        }
    }
}

// Kullanım örneği:
await Task.Run(() => { /* Arka plan işi */ });
MainThreadDispatcher.Enqueue(() =>
{
    // Bu kod ana iş parçacığında çalışır
    transform.position = Vector3.zero;
});

2. CancellationToken'ı Kontrol Etmeyi Unutmak

Hata: CancellationToken'ı bir Task'a iletmek, ancak Task içinde bu token'ın durumunu kontrol etmeyi veya ThrowIfCancellationRequested() metodunu çağırmayı unutmak. Bu durumda, iptal isteği gönderilse bile Task çalışmaya devam eder.

Çözüm: Uzun süren döngüler veya adımlar içeren Task'larınızda, periyodik olarak token.IsCancellationRequested özelliğini kontrol edin veya token.ThrowIfCancellationRequested() metodunu çağırın. Bu sayede, iptal isteği alındığında görev erken sonlandırılabilir.

3. CancellationTokenSource'u Dispose Etmemek

Hata: Bir CancellationTokenSource oluşturup kullanmak, ancak işiniz bittiğinde veya ilgili obje yok edildiğinde Dispose() metodunu çağırmayı unutmak. Bu durum, bellek sızıntılarına yol açabilir.

Çözüm: CancellationTokenSource, IDisposable arayüzünü uygular. Bu nedenle, kullanımdan sonra her zaman Dispose() metodunu çağırmalısınız. En iyi yöntem, using bloğu içinde kullanmak veya finally bloğunda manuel olarak Dispose() etmek ya da MonoBehaviour.OnDestroy() metodunda temizlik yapmak.

Performans ve Optimizasyon Notları

C# Task CancellationToken kullanımı, doğru yapıldığında performans artışı sağlar. Ancak bazı noktalara dikkat etmek önemlidir:

  • Gereksiz Task Oluşturmaktan Kaçının: Çok kısa süren veya zaten hafif olan işlemler için Task kullanmak, ek yük (overhead) getirebilir ve performansı olumsuz etkileyebilir. Task'lar, gerçekten uzun sürecek veya ana iş parçacığını bloke edecek işlemler için kullanılmalıdır.
  • Erken Çıkış ile Kaynak Tasarrufu: CancellationToken, bir görevin gereksiz yere devam etmesini engelleyerek CPU döngülerinden ve bellekten tasarruf etmenizi sağlar. İptal mekanizmasını doğru bir şekilde uygulamak, özellikle mobil platformlarda pil ömrü ve genel performans açısından kritik öneme sahiptir.
  • Thread Pool Kullanımı: Task.Run(), .NET'in iş parçacığı havuzundan (thread pool) bir iş parçacığı alır. Bu, her seferinde yeni bir iş parçacığı oluşturmanın maliyetinden kaçınmanızı sağlar. Ancak, iş parçacığı havuzunu aşırı yüklememeye dikkat edin; çok fazla eşzamanlı yoğun görev, sistemin genel performansını düşürebilir.

Unity'de C# Task CancellationToken yapılarını doğru bir şekilde anlamak ve uygulamak, daha kararlı, daha hızlı ve daha kullanıcı dostu oyunlar geliştirmenizin anahtarıdır. Asenkron programlama, modern uygulama geliştirmenin ayrılmaz bir parçasıdır ve Unity projelerinizde bu araçları ustaca kullanarak rekabet avantajı elde edebilirsiniz.

🧠 Ders Sonu Değerlendirme Testi

Dersi tamamladıktan sonra bilgilerinizi test edin ve ekstra puanlar kazanın.

🔥 +50 XP Ödül
🔒

Sınava Katılmak İçin Giriş Yapın

Bu ders sonu testini çözebilmek, bilginizi test edip **+50 XP** kazanmak ve **Sınav Şampiyonu** rozetinin kilidini açmak için üye girişi yapmalısınız.

Yorumlar (0)

Yorum yazabilmek ve derslere katkıda bulunabilmek için giriş yapmalısınız.

İlk yorumu siz yapın!