Chuỗi làm việc NSOperation và NSOperationQueue so với luồng chính


81

Tôi phải thực hiện một loạt các thao tác tải xuống và ghi cơ sở dữ liệu trong ứng dụng của mình. Tôi đang sử dụng NSOperationNSOperationQueuecho cùng một.

Đây là kịch bản ứng dụng:

  • Tìm nạp tất cả các mã bưu điện từ một nơi.
  • Đối với mỗi mã bưu điện tìm nạp tất cả các ngôi nhà.
  • Đối với mỗi ngôi nhà, tìm kiếm thông tin chi tiết về cư dân

Như đã nói, tôi đã xác định một NSOperationcho mỗi nhiệm vụ. Trong trường hợp đầu tiên (Task1), tôi đang gửi yêu cầu đến máy chủ để tìm nạp tất cả các mã bưu điện. Người được ủy quyền trong NSOperationsẽ nhận dữ liệu. Dữ liệu này sau đó được ghi vào cơ sở dữ liệu. Hoạt động cơ sở dữ liệu được định nghĩa trong một lớp khác. Từ NSOperationlớp, tôi đang gọi hàm ghi được định nghĩa trong lớp cơ sở dữ liệu.

Câu hỏi của tôi là liệu hoạt động ghi cơ sở dữ liệu xảy ra trong luồng chính hay trong một luồng nền? Khi tôi đang gọi nó trong một, NSOperationtôi đã mong đợi nó chạy trong một chuỗi khác (Không phải MainThread) như NSOperation. Ai đó có thể vui lòng giải thích kịch bản này trong khi xử lý NSOperationNSOperationQueue.


3
Nếu bạn thêm các thao tác vào hàng đợi chính, thì chúng sẽ được thực hiện trong luồng chính. Nếu bạn tạo NSOperationQueue của riêng mình và thêm các hoạt động vào nó, thì chúng sẽ được thực hiện trong các luồng của hàng đợi này.
Cy-4AH

1
Tôi không nghĩ rằng bạn sẽ nhận được câu trả lời tốt hơn @ Cy-4AH đã đưa ra trừ khi bạn nhận được mã cụ thể hơn / đăng một số mã. Tôi sẽ nói rằng bạn luôn có thể đặt một breakpoint trong các mã và khi nó chuyến đi nó sẽ hiển thị cho bạn những gì luồn dấu vết là trong.
Brad Allred

"Người được ủy quyền trong NSOperation sẽ nhận được dữ liệu là gì." nghĩa là? Không NSOperationhoặc không NSOperationQueuechứa thuộc tính đại biểu.
Jeffery Thomas

Bạn cũng có thể đẩy cuộc gọi đại biểu của mình lên chuỗi chính thay vì đưa ra bất kỳ giả định nào về chuỗi hiện tại ...
Wain

Câu trả lời:


175

Câu hỏi của tôi là liệu hoạt động ghi cơ sở dữ liệu xảy ra trong luồng chính hay trong một luồng nền?

Nếu bạn tạo NSOperationQueuetừ đầu như trong:

NSOperationQueue *myQueue = [[NSOperationQueue alloc] init];

Nó sẽ nằm trong một chuỗi nền:

Hàng đợi hoạt động thường cung cấp các luồng được sử dụng để chạy các hoạt động của chúng. Trong OS X v10.6 trở lên, các hàng đợi thao tác sử dụng thư viện libdispatch (còn được gọi là Grand Central Dispatch) để bắt đầu thực hiện các hoạt động của chúng. Do đó, các hoạt động luôn được thực hiện trên một chuỗi riêng biệt , bất kể chúng được chỉ định là hoạt động đồng thời hay không đồng thời

Trừ khi bạn đang sử dụng mainQueue:

NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];

Bạn cũng có thể thấy mã như thế này:

NSOperationQueue *myQueue = [[NSOperationQueue alloc] init];
[myQueue addOperationWithBlock:^{

   // Background work

    [[NSOperationQueue mainQueue] addOperationWithBlock:^{
        // Main thread work (UI usually)
    }];
}];

Và phiên bản GCD:

dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void)
             {
              // Background work            
             dispatch_async(dispatch_get_main_queue(), ^(void)
              {
                   // Main thread work (UI usually)                          
              });
});

NSOperationQueuecho phép kiểm soát tốt hơn những gì bạn muốn làm. Bạn có thể tạo phụ thuộc giữa hai thao tác (tải xuống và lưu vào cơ sở dữ liệu). Để chuyển dữ liệu giữa khối này và khối kia, bạn có thể giả sử ví dụ: a NSDatasẽ đến từ máy chủ, do đó:

__block NSData *dataFromServer = nil;
NSBlockOperation *downloadOperation = [[NSBlockOperation alloc] init];
__weak NSBlockOperation *weakDownloadOperation = downloadOperation;

[weakDownloadOperation addExecutionBlock:^{
 // Download your stuff  
 // Finally put it on the right place: 
 dataFromServer = ....
 }];

NSBlockOperation *saveToDataBaseOperation = [[NSBlockOperation alloc] init];
__weak NSBlockOperation *weakSaveToDataBaseOperation = saveToDataBaseOperation;

 [weakSaveToDataBaseOperation addExecutionBlock:^{
 // Work with your NSData instance
 // Save your stuff
 }];

[saveToDataBaseOperation addDependency:downloadOperation];

[myQueue addOperation:saveToDataBaseOperation];
[myQueue addOperation:downloadOperation];

Chỉnh sửa: Tại sao tôi sử dụng __weaktài liệu tham khảo cho Hoạt động, có thể được tìm thấy tại đây . Nhưng tóm lại là tránh chu kỳ giữ lại.


3
câu trả lời là đúng nhưng xin lưu ý rằng dòng mã chính xác là: NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];và NSOperationQueue trên luồng chính không thể bị treo.
Gianluca P.

2
@ jacky-boy Không quan tâm, tại sao lại sử dụng các tham chiếu yếu cho downloadOperation và saveToDataBaseOperation trong đoạn mã cuối cùng?
Thác nước Michael

RuiAAPeres, tôi tò mò tại sao lại có những tham chiếu yếu được sử dụng trong đoạn mã cuối cùng? Bạn có thể làm sáng tỏ điều đó được không?
Pavan

@Pavan ý tưởng là nếu bạn tình cờ tham chiếu cùng một khối hoạt động bên trong khối của chính nó, bạn sẽ có một chu kỳ lưu giữ. Tham khảo: conradstoll.com/blog/2013/1/19/…
Rui Peres

vậy khối nào giống khối nào chạy bên trong nó?
Pavan

16

Nếu bạn muốn thực hiện thao tác ghi cơ sở dữ liệu trong luồng nền, bạn cần tạo một NSManagedObjectContextluồng đó.

Bạn có thể tạo nền NSManagedObjectContexttrong phương thức bắt đầu của NSOperationlớp con có liên quan của mình .

Kiểm tra tài liệu của Apple để biết Đồng thời với Dữ liệu cốt lõi.

Bạn cũng có thể tạo một NSManagedObjectContextthực thi các yêu cầu trong luồng nền của chính nó bằng cách tạo NSPrivateQueueConcurrencyTypevà thực hiện các yêu cầu bên trong performBlock:phương thức của nó .


17
Điều gì khiến bạn nghĩ rằng câu hỏi này có liên quan đến Dữ liệu cốt lõi?
Nikolai Ruhe

11

Từ NSOperationQueue

Trong iOS 4 trở lên, hàng đợi thao tác sử dụng Grand Central Dispatch để thực hiện các thao tác. Trước iOS 4, họ tạo các luồng riêng biệt cho các hoạt động không đồng thời và khởi chạy các hoạt động đồng thời từ luồng hiện tại.

Vì thế,

[NSOperationQueue mainQueue] // added operations execute on main thread
[NSOperationQueue new] // post-iOS4, guaranteed to be not the main thread

Trong trường hợp của bạn, bạn có thể muốn tạo "chuỗi cơ sở dữ liệu" của riêng mình bằng cách phân lớp con NSThreadvà gửi tin nhắn đến nó bằng performSelector:onThread:.


cảm ơn rất nhiều ... u cứu mạng tôi: [NSOperationQueue new] // post-iOS4, được đảm bảo không phải là chủ đề chính
ikanimo

9

Chuỗi thực thi của NSOperation phụ thuộc vào NSOperationQueuenơi bạn đã thêm thao tác. Xem tuyên bố này trong mã của bạn -

[[NSOperationQueue mainQueue] addOperation:yourOperation]; // or any other similar add method of NSOperationQueue class

Tất cả điều này giả định rằng bạn đã không thực hiện thêm bất kỳ luồng nào trong mainphương thức NSOperationmà nó là con quái vật thực tế nơi các hướng dẫn công việc bạn có (dự kiến ​​sẽ được viết).

Tuy nhiên, trong trường hợp hoạt động đồng thời, kịch bản sẽ khác. Hàng đợi có thể sinh ra một luồng cho mỗi hoạt động đồng thời. Mặc dù nó không được bảo đảm và nó phụ thuộc vào tài nguyên hệ thống so với nhu cầu tài nguyên hoạt động tại thời điểm đó trong hệ thống. Bạn có thể kiểm soát sự đồng thời của hàng đợi hoạt động bằng thuộc tính của nó maxConcurrentOperationCount.

CHỈNH SỬA -

Tôi thấy câu hỏi của bạn thú vị và đã tự mình thực hiện một số phân tích / ghi nhật ký. Tôi đã tạo NSOperationQueue trên chuỗi chính như thế này -

self.queueSendMessageOperation = [[[NSOperationQueue alloc] init] autorelease];

NSLog(@"Operation queue creation. current thread = %@ \n main thread = %@", [NSThread currentThread], [NSThread mainThread]);
self.queueSendMessageOperation.maxConcurrentOperationCount = 1; // restrict concurrency

Và sau đó, tôi tiếp tục tạo NSOperation và thêm nó bằng addOperation. Trong phương pháp chính của thao tác này khi tôi kiểm tra luồng hiện tại,

NSLog(@"Operation obj =  %@\n current thread = %@ \n main thread = %@", self, [NSThread currentThread], [NSThread mainThread]);

nó không phải là chủ đề chính. Và, nhận thấy rằng đối tượng luồng hiện tại không phải là đối tượng luồng chính.

Vì vậy, việc tạo tùy chỉnh hàng đợi trên luồng chính (không có sự đồng thời giữa các hoạt động của nó) không nhất thiết có nghĩa là các hoạt động sẽ thực thi tuần tự trên chính luồng chính.


Tôi tin rằng việc sử dụng [[NSOperationQueue alloc] init]đảm bảo rằng hàng đợi sẽ sử dụng một DispatchQueue toàn cầu nền mặc định cơ bản. Vì vậy, bất kỳ OperationQueue được khởi tạo tùy chỉnh nào cũng sẽ cung cấp các tác vụ của nó vào DispatchQueue nền và do đó cấp các tác vụ vào các luồng không phải là luồng chính. Để đưa các hoạt động vào chuỗi chính, bạn sẽ phải lấy OperationQueue chính bằng cách sử dụng một cái gì đó như [NSOperationQueue mainQueue]. Thao tác này thu được hàng đợi hoạt động chính sử dụng DispatchQueue chính trong nội bộ và do đó cuối cùng cung cấp các tác vụ vào luồng chính.
Gordonium

1

Tóm tắt từ tài liệu là operations are always executed on a separate thread(bài đăng iOS 4 ngụ ý hàng đợi hoạt động cơ bản của GCD).

Thật đơn giản khi kiểm tra xem nó có thực sự đang chạy trên một chuỗi không chính hay không:

NSLog(@"main thread? %@", [NSThread isMainThread] ? @"YES" : @"NO");

Khi chạy trong một luồng, việc sử dụng GCD / libdispatch để chạy một cái gì đó trên luồng chính, cho dù dữ liệu cốt lõi, giao diện người dùng hoặc mã khác được yêu cầu để chạy trên luồng chính là rất nhỏ:

dispatch_async(dispatch_get_main_queue(), ^{
    // this is now running on the main thread
});

-2

Nếu bạn đang thực hiện bất kỳ luồng không tầm thường nào, bạn nên sử dụng FMDatabaseQueue .


1
Câu hỏi không đề cập đến một cơ sở dữ liệu cụ thể, như fmdb hoặc sqlite.
Nikolai Ruhe

Điều đó đúng, tôi đã đưa ra một giả định dựa trên ngữ cảnh. Nếu ứng dụng đang kết nối với cơ sở dữ liệu an toàn theo luồng, nhiều người thuê, chẳng hạn như MySQL, thì câu hỏi sẽ không có ý nghĩa. Có những cơ sở dữ liệu người dùng đơn khác có thể được sử dụng, nhưng SQLite là cơ sở dữ liệu phổ biến nhất cho đến nay.
Holly
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.