2010-06-29 23 views
6

Tôi đang sử dụng FMDB để xử lý cơ sở dữ liệu của tôi hoạt động tốt. Ứng dụng sử dụng chuỗi nền đang thực hiện một số công việc và cần truy cập cơ sở dữ liệu. Đồng thời, chủ đề chính cần chạy một số truy vấn trên cùng một cơ sở dữ liệu. Bản thân FMDB có một hệ thống khóa nhỏ, tuy nhiên, tôi đã thêm một hệ thống khác vào các lớp của tôi.EXC_BAD_ACCESS khi sử dụng SQLite (FMDB) và các chủ đề trên iOS 4.0

Mọi truy vấn chỉ được thực hiện nếu lớp của tôi chỉ ra rằng cơ sở dữ liệu không được sử dụng. Sau khi thực hiện các hành động, cơ sở dữ liệu sẽ được mở khóa. Điều này hoạt động như mong đợi miễn là tải không quá cao. Khi tôi truy cập nhiều dữ liệu với chuỗi chạy trên luồng chính, lỗi EXC_BAD_ACCESS xảy ra.

Dưới đây là tìm kiếm:

- (BOOL)isDatabaseLocked { 
    return isDatabaseLocked; 
} 

- (Pile *)lockDatabase { 
    isDatabaseLocked = YES; 
    return self;   
} 

- (FMDatabase *)lockedDatabase { 
    @synchronized(self) { 
     while ([self isDatabaseLocked]) { 
      usleep(20); 
      //NSLog(@"Waiting until database gets unlocked..."); 
     } 
     isDatabaseLocked = YES; 
     return self.database;  
    } 
} 

- (Pile *)unlockDatabase { 
    isDatabaseLocked = NO; 
    return self;    
} 

Các debugger nói rằng lỗi xảy ra tại [FMResultSet next] tại dòng

rc = sqlite3_step(statement.statement); 

Tôi đôi kiểm tra tất cả giữ lại đếm và tất cả các đối tượng vẫn tồn tại vào thời điểm này. Một lần nữa, nó chỉ xảy ra khi chủ đề chính bắt đầu nhiều truy vấn trong khi luồng nền đang chạy (chính nó luôn tạo ra tải nặng). Lỗi luôn luôn được tạo ra bởi luồng chính, không bao giờ bởi chuỗi nền.

Ý tưởng cuối cùng của tôi là cả hai luồng đều chạy khóaDatabase cùng một lúc để chúng có thể nhận được đối tượng cơ sở dữ liệu. Đó là lý do tại sao tôi đã thêm khóa mutex qua "@synchronized (self)". Tuy nhiên, điều này không giúp được gì.

Có ai có đầu mối không?

+0

chủ đề này cho một vấn đề FMDB đưa ra một số cái nhìn sâu sắc khác hữu ích vào nguyên nhân có thể: https://github.com/ccgus/fmdb/issues/39 –

Trả lời

2

Bạn nên thêm trình bao bọc đồng bộ hóa xung quanh chức năng của bạn mở khóaDatabase và lockDatabase, cũng như isDatabaseLocked - không phải lúc nào cũng được bảo đảm rằng một cửa hàng hoặc truy lục của biến là nguyên tử. Tất nhiên, nếu bạn làm bạn sẽ muốn di chuyển giấc ngủ của bạn bên ngoài khối đồng bộ, nếu không bạn sẽ bế tắc. Đây thực chất là một khóa xoay - nó không phải là phương pháp hiệu quả nhất.

- (FMDatabase *)lockedDatabase { 
    do 
    { 
     @synchronized(self) { 
      if (![self isDatabaseLocked]) { 
       isDatabaseLocked = YES; 
       return self.database; 
      } 
     } 
     usleep(20);  
    }while(true); // continue until we get a lock 
} 

Bạn có chắc chắn rằng bạn không sử dụng đối tượng FMDatabase sau khi được gọi là unlockDatabase không? Bạn có thể muốn xem xét một mẫu xử lý - tạo một đối tượng bao bọc đối tượng FMDatabase và miễn là nó tồn tại, giữ một khóa trên cơ sở dữ liệu. Trong init bạn yêu cầu khóa, và trong dealloc, bạn có thể giải phóng khóa đó. Sau đó, mã khách hàng của bạn không cần phải lo lắng về việc gọi các chức năng khóa/mở khóa khác nhau và bạn sẽ không vô tình bị trục trặc. Hãy thử sử dụng NSMutex thay vì các khối không đồng bộ hóa, hãy xem http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/Multithreading/ThreadSafety/ThreadSafety.html#//apple_ref/doc/uid/10000057i-CH8-SW16

+0

Tôi tìm thấy một đoạn mã mà tôi truy cập vào cơ sở dữ liệu trực tiếp. Vì vậy, nó đã không bị khóa. Sau khi khắc phục vấn đề nhỏ này, mọi thứ hoạt động hoàn toàn tốt đẹp. – danielkbx

+0

Bạn khắc phục sự cố này như thế nào? Bạn có thể gửi mã giúp? – Split

+0

Tôi sử dụng mã mà tôi đã đăng ban đầu. Lỗi của tôi là ứng dụng đã thực hiện truy vấn cơ sở dữ liệu mà không cần khóa nên không gọi khóaDDabase. – danielkbx

0

Bạn cũng có thể thử FMDatabaseQueue - Tôi đã tạo riêng cho các tình huống như thế này. Tôi đã không thử nó, nhưng tôi khá chắc chắn nó sẽ làm việc cho iOS 4.

+0

Tôi nghĩ rằng FMDatabaseQueue chỉ hoạt động cho các truy vấn, đúng không? Có cách nào đơn giản để thực hiện tương tự cho các cập nhật (ngoài cài đặt SQLite ở trên) không? –

6

SQLite cung cấp một serialization đơn giản hơn nhiều. Chỉ cần thiết lập tùy chọn sqlite_config() SQLITE_CONFIG_SERIALIZED, bạn có lẽ sẽ tránh được hầu hết các loại nhức đầu này. Tôi phát hiện ra điều này một cách khó khăn sau khi chiến đấu với các vấn đề luồng trong một thời gian dài.

Đây là cách bạn sử dụng nó, bạn có thể đặt nó trong phương thức init của FMDatabase ...

if (sqlite3_config(SQLITE_CONFIG_SERIALIZED) == SQLITE_ERROR) { 
     NSLog(@"couldn't set serialized mode"); 
    } 

Xem các tài liệu SQLite trên threadsafetyserialized mode để biết thêm.

+1

CẢM ƠN BẠN! Cảm ơn bạn, cảm ơn bạn, cảm ơn bạn. Đây chính xác là những gì tôi cần! – DOOManiac

+0

Tài liệu SQLite nói rằng nó mặc định là chế độ tuần tự hóa, nhưng có vẻ như phiên bản của thư viện đi kèm với MacOS được đặt mặc định thành chế độ một luồng (mà tôi đã phát hiện ra một cách khó khăn). trên Linux và Windows cho Mac). –

+0

Các bạn, bạn có thể giải thích cho tôi một điều không? SQLITE_CONFIG_SERIALIZED đảm bảo bật công cụ mutex bên trong sqlite, tuyệt vời. Vì vậy, tất cả các phương pháp của nó là nguyên tử, phải không? Nhưng ai có thể đảm bảo rằng mọi thứ diễn ra đúng trong các phương thức của khách hàng trong một hàng đợi đồng thời? Ví, ví dụ chúng ta gọi là void foo() { sqlite3_open() sqlite3_exec() sqlite3_next_stmt() sqlite3_finalize() sqlite3_close() } Mỗi cuộc gọi bên trong phương pháp này là nguyên tử và an toàn bên trong. Nhưng khi chúng ta gọi 'foo' từ các phương thức chủ đề khác nhau được gọi là hỗn loạn. Giống như 'hoàn thành' từ chuỗi 1 sau khi 'mở' từ chuỗi 2 và cứ tiếp tục như vậy. –

0

Tôi đã gặp sự cố này và có thể loại bỏ sự cố chỉ bằng cách bật bộ nhớ đệm của các câu lệnh đã chuẩn bị.

FMDatabase *myDatabase = [FMDatabase databaseWithPath: pathToDatabase]; 
myDatabase.shouldCacheStatements = YES; 
Các vấn đề liên quan