Câu hỏi NSOperation và NSOperationQueue thread làm việc vs chủ đề chính


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

Đâ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 địa điểm.
  • Đối với mỗi mã bưu điện, hãy tìm nạp tất cả các ngôi nhà.
  • Đối với mỗi ngôi nhà, hãy tìm chi tiết về cư dân

Như đã nói, tôi đã xác định NSOperation cho mỗi công việc. 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 NSOperation sẽ 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ừ NSOperation lớp Tôi đang thực hiện cuộc gọi đến hàm write đượ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 có xảy ra trong chủ đề chính hay trong một luồng nền? Như tôi đã gọi nó trong một NSOperation Tôi đã hy vọng nó chạy trong một chủ đề khác nhau (không phải là MainThread) là NSOperation. Ai đó có thể vui lòng giải thích kịch bản này trong khi xử lý NSOperation và NSOperationQueue.


76
2017-10-24 14:49


gốc


Nếu bạn thêm hoạt động vào hàng đợi chính, thì chúng sẽ được thực hiện trong chuỗi chính. Nếu bạn tạo NSOperationQueue của riêng bạn 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
Tôi không nghĩ rằng bạn sẽ nhận được một câu trả lời tốt hơn so với @ Cy-4AH đã cho trừ khi bạn nhận được 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 điểm ngắt trong mã và khi nó đi nó sẽ cho bạn thấy những gì các dấu vết là trong. - Brad Allred
"Các đại biểu trong NSOperation sẽ nhận được dữ liệu." nghĩa là? Cũng không NSOperation cũng không NSOperationQueue chứa các thuộc tính ủy nhiệm. - Jeffery Thomas
Bạn cũng có thể đẩy cuộc gọi đại biểu của bạn vào chủ đề chính thay vì đưa ra bất kỳ giả định nào về chuỗi hiện tại ... - Wain


Các câu trả lời:


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

Nếu bạn tạo một NSOperationQueue từ đầu như sau:

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

Nó sẽ nằm trong một chủ đề nền:

Hàng đợi hoạt động thường cung cấp các chủ đề được sử dụng để chạy   hoạt động. Trong OS X v10.6 trở lên, hàng đợi hoạt động sử dụng   thư viện libdispatch (còn được gọi là Grand Central Dispatch) để bắt đầu   việc thực hiện các hoạt động của họ. Kết quả là, các hoạt động luôn   thực hiện trên một sợi riêng biệt, bất kể họ là   được chỉ định là hoạt động đồng thời hoặc 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ư sau:

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)                          
              });
});

NSOperationQueue cho phép kiểm soát tốt hơn với 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 một khối và một khối khác, bạn có thể giả định ví dụ, NSData sẽ đến từ máy chủ để:

__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 đang sử dụng __weak tham khảo cho các hoạt động, có thể được tìm thấy đây. Nhưng trong một nutshell là để tránh giữ chu kỳ.


164
2017-11-02 21:07



câu trả lời đúng nhưng xin lưu ý rằng dòng mã chính xác là: NSOperationQueue *mainQueue = [NSOperationQueue mainQueue]; và rằng NSOperationQueue trên chủ đề chính không thể bị đình chỉ. - Gianluca P.
@ jacky-boy Không quan tâm, tại sao việc sử dụng các tham chiếu yếu đến downloadOperation và saveToDataBaseOperation trong đoạn mã cuối cùng? - Michael Waterfall
RuiAAPeres, hey, tôi tò mò tại sao 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 đó không? - Pavan
@ Java ý tưởng là nếu bạn xảy ra để tham khảo các hoạt động cùng một khối bên trong khối riêng của nó, bạn có một chu kỳ giữ lại. Để tham khảo: conradstoll.com/blog/2013/1/19/… - Peres
vì vậy khối nào giống như khối chạy bên trong khối đó? - Pavan


Nếu bạn muốn thực hiện thao tác ghi cơ sở dữ liệu trong chuỗi nền, bạn cần tạo NSManagedObjectContext cho chủ đề đó.

Bạn có thể tạo nền NSManagedObjectContext trong phương pháp bắt đầu của bạn có liên quan NSOperation lớp con.

Kiểm tra tài liệu của Apple cho Đồng thời với dữ liệu lõi.

Bạn cũng có thể tạo một NSManagedObjectContext thực hiện các yêu cầu trong chuỗi nền của riêng nó bằng cách tạo ra nó với NSPrivateQueueConcurrencyType và thực hiện các yêu cầu bên trong performBlock: phương pháp.


16
2017-10-30 09:31



Đ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


Từ NSOperationQueue

Trong iOS 4 trở lên, hàng đợi hoạt động sử dụng Grand Central Dispatch để thực thi các thao tác. Trước iOS 4, chúng 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 bạn bằng cách phân lớp con NSThread và gửi tin nhắn cho nó với performSelector:onThread:.


9
2017-11-01 13:32



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


Chuỗi thực hiện của NSOperation phụ thuộc vào NSOperationQueue nơi bạn đã thêm hoạt động. Xem câu lệnh 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 bạn đã không thực hiện bất kỳ luồng khác trong main phương pháp của NSOperation đó là con quái vật thực sự nơi mà các hướng dẫn công việc bạn có (dự kiến ​​sẽ được) bằng văn bản.

Tuy nhiên, trong trường hợp hoạt động đồng thời, kịch bản là khác nhau. Hàng đợi có thể sinh ra một luồng cho mỗi thao tác đồng thời. Mặc dù nó không phải là guarrantteed 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 đồng thời hàng đợi hoạt động của nó maxConcurrentOperationCount bất động sản.

CHỈNH SỬA - -

Tôi tìm thấy câu hỏi của bạn thú vị và đã làm một số phân tích / đăng nhập bản thân mình. Tôi đã tạo NSOperationQueue trên chủ đề 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 ra một NSOperation và thêm nó bằng cách sử dụng addOperation. Trong phương thức 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à, thấy rằng đối tượng thread hiện tại không phải là đối tượng thread chính.

Vì vậy, tùy chỉnh tạo hàng đợi trên chủ đề chính (không có sự tương tranh 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 serially trên chính thread.


9
2017-10-30 05:07



Tôi tin rằng sử dụng [[NSOperationQueue alloc] init] đảm bảo rằng hàng đợi sẽ sử dụng một nền tảng mặc định toàn cục DispatchQueue. Vì vậy, bất kỳ tùy chỉnh khởi tạo OperationQueue sẽ ăn nhiệm vụ của mình lên nền DispatchQueues và do đó nguồn cấp dữ liệu vào các chủ đề không phải là chủ đề chính. Để nạp các hoạt động vào luồng chính, bạn sẽ phải có được OperationQueue chính bằng cách sử dụng một cái gì đó như [NSOperationQueue mainQueue]. Điều này có đượ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 nhiệm vụ lên luồng chính. - Gordonium


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

Đó là tầm thường để kiểm tra xem nó thực sự đang chạy trên một chủ đề không chính:

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

Khi chạy trong một chủ đề, nó không quan trọng để 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 hay mã khác cần thiết để chạy trên luồng chính:

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

1
2017-11-02 19:52





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.


-2
2017-10-30 02:10



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
Đó là chính xác, 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 đa chủ đề, an toàn, chẳng hạn như MySQL, câu hỏi sẽ không có ý nghĩa. Có những cơ sở dữ liệu người dùng đơn lẻ khác có thể được sử dụng, nhưng SQLite là phổ biến nhất cho đến nay. - Holly